/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2021 Motorcomm Corporation. */

#include "fuxi-os.h"
#include "fuxi-gmac.h"
#include "fuxi-gmac-reg.h"
#include "fuxi-efuse.h"

void fxgmac_release_phy(struct fxgmac_pdata *pdata);
static void fxgmac_pwr_clock_ungate(struct fxgmac_pdata *pdata);
static void fxgmac_pwr_clock_gate(struct fxgmac_pdata *pdata);

static int fxgmac_tx_complete(struct fxgmac_dma_desc *dma_desc)
{
#if (FXGMAC_DUMMY_TX_DEBUG)
	return 1;
#endif
	return !FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS,
				       TX_NORMAL_DESC3_OWN_LEN);
}

static int fxgmac_disable_rx_csum(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	DPRINTK("fxgmac disable rx checksum.\n");
	return 0;
}

static int fxgmac_enable_rx_csum(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	DPRINTK("fxgmac enable rx checksum.\n");
	return 0;
}

static int fxgmac_set_mac_address(struct fxgmac_pdata *pdata, u8 *addr)
{
	unsigned int mac_addr_hi, mac_addr_lo;

	mac_addr_hi = (addr[5] << 8) | (addr[4] << 0);
	mac_addr_lo = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) |
		      (addr[0] << 0);

	writereg(pdata->pAdapter, mac_addr_hi, pdata->mac_regs + MAC_MACA0HR);
	writereg(pdata->pAdapter, mac_addr_lo, pdata->mac_regs + MAC_MACA0LR);

	return 0;
}

#if !defined(DPDK)
static void fxgmac_set_mac_reg(struct fxgmac_pdata *pdata,
			       struct netdev_hw_addr *ha, unsigned int *mac_reg)
{
	unsigned int mac_addr_hi, mac_addr_lo;
	u8 *mac_addr;

	mac_addr_lo = 0;
	mac_addr_hi = 0;

	if (ha) {
		mac_addr = (u8 *)&mac_addr_lo;
		mac_addr[0] = ha->addr[0];
		mac_addr[1] = ha->addr[1];
		mac_addr[2] = ha->addr[2];
		mac_addr[3] = ha->addr[3];
		mac_addr = (u8 *)&mac_addr_hi;
		mac_addr[0] = ha->addr[4];
		mac_addr[1] = ha->addr[5];

		netif_dbg(pdata, drv, pdata->netdev,
			  "adding mac address %pM at %#x\n", ha->addr,
			  *mac_reg);

		mac_addr_hi = FXGMAC_SET_REG_BITS(
			mac_addr_hi, MAC_MACA1HR_AE_POS, MAC_MACA1HR_AE_LEN, 1);
	}

	writereg(pdata->pAdapter, mac_addr_hi, pdata->mac_regs + *mac_reg);
	*mac_reg += MAC_MACA_INC;
	writereg(pdata->pAdapter, mac_addr_lo, pdata->mac_regs + *mac_reg);
	*mac_reg += MAC_MACA_INC;
}
#endif

static int fxgmac_enable_tx_vlan(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANIR);
	/* Indicate that VLAN Tx CTAGs come from mac_vlan_incl register */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLTI_POS,
				     MAC_VLANIR_VLTI_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_CSVL_POS,
				     MAC_VLANIR_CSVL_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLP_POS,
				     MAC_VLANIR_VLP_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLC_POS,
				     MAC_VLANIR_VLC_LEN, 2);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLT_POS,
				     MAC_VLANIR_VLT_LEN, pdata->vlan);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANIR);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS,
				     MAC_VLANTR_VL_LEN, pdata->vlan);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR);

	return 0;
}

static int fxgmac_disable_tx_vlan(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANIR);

	/* Indicate that VLAN Tx CTAGs come from mac_vlan_incl register
	 * Set VLAN Tag input enable
	 */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_CSVL_POS,
				     MAC_VLANIR_CSVL_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLTI_POS,
				     MAC_VLANIR_VLTI_LEN, /*0*/ 1);
	/* Set VLAN priority control disable */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLP_POS,
				     MAC_VLANIR_VLP_LEN, /*1*/ 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLC_POS,
				     MAC_VLANIR_VLC_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANIR);

	return 0;
}

static int fxgmac_enable_rx_vlan_stripping(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	/* Put the VLAN tag in the Rx descriptor */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLRXS_POS,
				     MAC_VLANTR_EVLRXS_LEN, 1);
	/* Don't check the VLAN type */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_DOVLTC_POS,
				     MAC_VLANTR_DOVLTC_LEN, 1);
	/* Check only C-TAG (0x8100) packets */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ERSVLM_POS,
				     MAC_VLANTR_ERSVLM_LEN, 0);
	/* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ESVL_POS,
				     MAC_VLANTR_ESVL_LEN, 0);
	/* Enable VLAN tag stripping */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS,
				     MAC_VLANTR_EVLS_LEN, 0x3);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR);
	DPRINTK("fxgmac enable MAC rx vlan stripping.\n");

	return 0;
}

static int fxgmac_disable_rx_vlan_stripping(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS,
				     MAC_VLANTR_EVLS_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR);
	DPRINTK("fxgmac disable MAC rx vlan stripping.\n");

	return 0;
}

static int fxgmac_enable_rx_vlan_filtering(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);
	/* Enable VLAN filtering */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN,
				     1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);

#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS,
				     MAC_VLANTR_VL_LEN, pdata->vlan);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR);
#else
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	/* Enable VLAN Hash Table filtering */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTHM_POS,
				     MAC_VLANTR_VTHM_LEN, 1);
	/* Disable VLAN tag inverse matching */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTIM_POS,
				     MAC_VLANTR_VTIM_LEN, 0);
	/* Only filter on the lower 12-bits of the VLAN tag */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ETV_POS,
				     MAC_VLANTR_ETV_LEN, 1);
#endif

	return 0;
}

static int fxgmac_disable_rx_vlan_filtering(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);
	/* Disable VLAN filtering */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN,
				     0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);

#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS,
				     MAC_VLANTR_VL_LEN, pdata->vlan);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR);
#endif

	return 0;
}

#if FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED
static u32 fxgmac_vid_crc32_le(__le16 vid_le)
{
	unsigned char *data = (unsigned char *)&vid_le;
	unsigned char data_byte = 0;
	u32 crc = ~0;
	u32 temp = 0;
	int i, bits;

	bits = get_bitmask_order(VLAN_VID_MASK);
	for (i = 0; i < bits; i++) {
		if ((i % 8) == 0)
			data_byte = data[i / 8];

		temp = ((crc & 1) ^ data_byte) & 1;
		crc >>= 1;
		data_byte >>= 1;

		if (temp)
			crc ^= CRC32_POLY_LE;
	}

	return crc;
}
#endif

static int fxgmac_update_vlan_hash_table(struct fxgmac_pdata *pdata)
{
	u16 vlan_hash_table = 0;
	u32 regval;
#if FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED
	__le16 vid_le;
	u32 crc;
	u16 vid;
	/* Generate the VLAN Hash Table value */
	for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) {
		/* Get the CRC32 value of the VLAN ID */
		vid_le = cpu_to_le16(vid);
		crc = bitrev32(~fxgmac_vid_crc32_le(vid_le)) >> 28;

		vlan_hash_table |= (1 << crc);
	}
#endif
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANHTR);
	/* Set the VLAN Hash Table filtering register */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANHTR_VLHT_POS,
				     MAC_VLANHTR_VLHT_LEN, vlan_hash_table);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANHTR);

	DPRINTK("fxgmac_update_vlan_hash_tabl done, hash tbl=%08x.\n",
		vlan_hash_table);
	return 0;
}

static int fxgmac_set_promiscuous_mode(struct fxgmac_pdata *pdata,
				       unsigned int enable)
{
	unsigned int val = enable ? 1 : 0;
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);

	if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_PR_POS, MAC_PFR_PR_LEN) ==
	    val) {
		return 0;
	}
	netif_dbg(pdata, drv, pdata->netdev,
		  "" STR_FORMAT " promiscuous mode\n",
		  enable ? "entering" : "leaving");

	regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_PR_POS, MAC_PFR_PR_LEN,
				     val);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);

	DbgPrintF(MP_TRACE, "" STR_FORMAT " - promiscuous mode=%d, reg=%x.",
		  __FUNCTION__, enable, regval);
	DbgPrintF(
		MP_TRACE,
		"" STR_FORMAT
		" - note, vlan filter is called when set promiscuous mode=%d.",
		__FUNCTION__, enable);

	/* Hardware will still perform VLAN filtering in promiscuous mode */
	if (enable) {
		fxgmac_disable_rx_vlan_filtering(pdata);
	} else {
		if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
			fxgmac_enable_rx_vlan_filtering(pdata);
		}
	}

	DPRINTK("fxgmac set promisc mode=%d\n", enable);
	return 0;
}

static int fxgmac_enable_rx_broadcast(struct fxgmac_pdata *pdata,
				      unsigned int enable)
{
	/* mac reg bit is disable, so invert the val. */
	unsigned int val = enable ? 0 : 1;
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);

	if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN) ==
	    val) {
		return 0;
	}

	regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN,
				     val);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);

	DbgPrintF(MP_TRACE, "%s - bcast en=%d, bit-val=%d, reg=%x.",
		  __FUNCTION__, enable, val, regval);
	return 0;
}

static int fxgmac_set_all_multicast_mode(struct fxgmac_pdata *pdata,
					 unsigned int enable)
{
	unsigned int val = enable ? 1 : 0;
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);
	if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_PM_POS, MAC_PFR_PM_LEN) ==
	    val) {
		return 0;
	}
	netif_dbg(pdata, drv, pdata->netdev, "" STR_FORMAT " allmulti mode\n",
		  enable ? "entering" : "leaving");

	regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_PM_POS, MAC_PFR_PM_LEN,
				     val);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);

	DbgPrintF(MP_TRACE,
		  "" STR_FORMAT " - Enable all Multicast=%d, regval=%#x.",
		  __FUNCTION__, enable, regval);

	return 0;
}

static void fxgmac_set_mac_addn_addrs(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
#if FXGMAC_FILTER_MULTIPLE_MAC_ADDR_ENABLED
	struct net_device *netdev = pdata->netdev;
	struct netdev_hw_addr *ha;
#endif
	unsigned int addn_macs;
	unsigned int mac_reg;

	mac_reg = MAC_MACA1HR;
	addn_macs = pdata->hw_feat.addn_mac;
#if FXGMAC_FILTER_MULTIPLE_MAC_ADDR_ENABLED
	DPRINTK("xlgamc add mac addr callin\n");
	if (netdev_uc_count(netdev) > addn_macs) {
		fxgmac_set_promiscuous_mode(pdata, 1);
	} else {
		netdev_for_each_uc_addr(ha, netdev) {
			fxgmac_set_mac_reg(pdata, ha, &mac_reg);
			addn_macs--;
		}

		if (netdev_mc_count(netdev) > addn_macs) {
			fxgmac_set_all_multicast_mode(pdata, 1);
		} else {
			netdev_for_each_mc_addr(ha, netdev) {
				fxgmac_set_mac_reg(pdata, ha, &mac_reg);
				addn_macs--;
			}
		}
	}
#endif
	/* Clear remaining additional MAC address entries */
	while (addn_macs--) {
		fxgmac_set_mac_reg(pdata, NULL, &mac_reg);
	}
#else
	(void)pdata;
#endif
}

#define GET_REG_AND_BIT_POS(reversalval, regOut, bitOut) \
	do {                                             \
		regOut = (((reversalval) >> 5) & 0x7);   \
		bitOut = ((reversalval) & 0x1f);         \
	} while (0)

static u32 fxgmac_crc32(unsigned char *Data, int Length)
{
	u32 Crc = (u32)~0; /* Initial value. 0xFFFFFFFF */

	while (--Length >= 0) {
		unsigned char Byte = *Data++;
		int Bit;

		for (Bit = 8; --Bit >= 0; Byte >>= 1) {
			if ((Crc ^ Byte) & 1) {
				Crc >>= 1;
				Crc ^= 0xedb88320;
			} else {
				Crc >>= 1;
			}
		}
	}

	return ~Crc;
}

/*
 * configure multicast hash table, reg 0x2010~202c
 * input:     pmc_mac, pointer to mcast MAC. if it is null, then clean all registers.
 *         b_add, 1 to set the bit; 0 to clear the bit.
 */
static void fxgmac_config_multicast_mac_hash_table(struct fxgmac_pdata *pdata,
						   unsigned char *pmc_mac,
						   int b_add)
{
	unsigned int hash_reg, reg_bit;
	unsigned int j;
	u32 crc, reversal_crc, regval;

	if (!pmc_mac) {
		for (j = 0; j < FXGMAC_MAC_HASH_TABLE_SIZE; j++) {
			hash_reg = j;
			hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC);
			writereg(pdata->pAdapter, 0,
				 pdata->mac_regs + hash_reg);
		}
		DBGPRINT(
			MP_TRACE,
			("<fxgmac_config_mcast_mac_hash_table, clear all mcast mac hash table\n"));
		return;
	}

	crc = fxgmac_crc32(pmc_mac, ETH_ALEN);

	/* reverse the crc */
	for (j = 0, reversal_crc = 0; j < 32; j++) {
		if (crc & (1 << j))
			reversal_crc |= 1 << (31 - j);
	}

	GET_REG_AND_BIT_POS((reversal_crc >> 24), hash_reg, reg_bit);
	/* Set the MAC Hash Table registers */
	hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC);
	regval = readreg(pdata->pAdapter, pdata->mac_regs + hash_reg);

	regval = FXGMAC_SET_REG_BITS(regval, reg_bit, 1, (b_add ? 1 : 0));

	writereg(pdata->pAdapter, regval, pdata->mac_regs + hash_reg);
}

static void fxgmac_set_mac_hash_table(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
#if FUXI_MAC_HASH_TABLE
	struct net_device *netdev = pdata->netdev;
	struct netdev_hw_addr *ha;

	netdev_for_each_mc_addr(ha, netdev) {
		fxgmac_config_multicast_mac_hash_table(pdata, ha->addr, 1);
	}
#endif
	pdata = pdata;

#else
	(void)pdata;
#endif
}

static int fxgmac_add_mac_addresses(struct fxgmac_pdata *pdata)
{
	if (pdata->hw_feat.hash_table_size)
		fxgmac_set_mac_hash_table(pdata);
	else
		fxgmac_set_mac_addn_addrs(pdata);

	return 0;
}

static void fxgmac_config_mac_address(struct fxgmac_pdata *pdata)
{
	u32 regval;
	fxgmac_set_mac_address(pdata, pdata->mac_addr);

	/* Filtering is done using perfect filtering and hash filtering */
	if (pdata->hw_feat.hash_table_size) {
		regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR);
		regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HPF_POS,
					     MAC_PFR_HPF_LEN, 1);
#if FUXI_MAC_HASH_TABLE
		regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HUC_POS,
					     MAC_PFR_HUC_LEN, 1);
#endif
		regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HMC_POS,
					     MAC_PFR_HMC_LEN, 1);
		writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR);
	}
}

static int fxgmac_config_crc_check(struct fxgmac_pdata *pdata)
{
	u32 regval, value;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ECR);
	value = (pdata->crc_check) ? 0 : 1;
	regval = FXGMAC_SET_REG_BITS(regval, MAC_ECR_DCRCC_POS,
				     MAC_ECR_DCRCC_LEN, value);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_ECR);

	return 0;
}

static int fxgmac_config_jumbo(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_JE_POS, MAC_CR_JE_LEN,
				     pdata->jumbo);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);
	return 0;
}

static void fxgmac_config_checksum_offload(struct fxgmac_pdata *pdata)
{
	if (pdata->netdev->features & NETIF_F_RXCSUM)
		fxgmac_enable_rx_csum(pdata);
	else
		fxgmac_disable_rx_csum(pdata);
}

static void fxgmac_config_vlan_support(struct fxgmac_pdata *pdata)
{
	fxgmac_disable_tx_vlan(
		pdata); /* configure dynamical vlanID from TX Context. */

	/* Set the current VLAN Hash Table register value */
	fxgmac_update_vlan_hash_table(pdata);

	if (pdata->vlan_filter) /* disable vlan rx filter by default */
		fxgmac_enable_rx_vlan_filtering(pdata);
	else
		fxgmac_disable_rx_vlan_filtering(pdata);

	if (pdata->vlan_strip) /* enable vlan rx strip by default */
		fxgmac_enable_rx_vlan_stripping(pdata);
	else
		fxgmac_disable_rx_vlan_stripping(pdata);
}

static int fxgmac_config_rx_mode(struct fxgmac_pdata *pdata)
{
	struct net_device *netdev = pdata->netdev;
	unsigned int pr_mode, am_mode;

	pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
	am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);

	fxgmac_set_promiscuous_mode(pdata, pr_mode);
	fxgmac_set_all_multicast_mode(pdata, am_mode);

	fxgmac_add_mac_addresses(pdata);

	return 0;
}

static void fxgmac_prepare_tx_stop(struct fxgmac_pdata *pdata,
				   struct fxgmac_channel *channel)
{
	unsigned int tx_dsr, tx_pos, tx_qidx;
	unsigned long tx_timeout;
	unsigned int tx_status;

	pdata = pdata;

	/* Calculate the status register to read and the position within */
	if (channel->queue_index < DMA_DSRX_FIRST_QUEUE) {
		tx_dsr = DMA_DSR0;
		tx_pos = (channel->queue_index * DMA_DSR_Q_LEN) +
			 DMA_DSR0_TPS_START;
	} else {
		tx_qidx = channel->queue_index - DMA_DSRX_FIRST_QUEUE;

		tx_dsr = DMA_DSR1 + ((tx_qidx / DMA_DSRX_QPR) * DMA_DSRX_INC);
		tx_pos = ((tx_qidx % DMA_DSRX_QPR) * DMA_DSR_Q_LEN) +
			 DMA_DSRX_TPS_START;
	}

#if FXGMAC_TX_HANG_TIMER_EN
	tx_timeout = jiffies + msecs_to_jiffies(100); /* 100ms */
#else
	tx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ);
#endif
	while (time_before(jiffies, tx_timeout)) {
		tx_status = readreg(pdata->pAdapter, pdata->mac_regs + tx_dsr);
		tx_status =
			FXGMAC_GET_REG_BITS(tx_status, tx_pos, DMA_DSR_TPS_LEN);
		if ((tx_status == DMA_TPS_STOPPED) ||
		    (tx_status == DMA_TPS_SUSPENDED))
			break;

		usleep_range_ex(pdata->pAdapter, 500, 1000);
	}

	if (!time_before(jiffies, tx_timeout))
		netdev_info(pdata->netdev,
			    "timed out waiting for Tx DMA channel %u to stop\n",
			    channel->queue_index);
}

static void fxgmac_enable_tx(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
#endif
	unsigned int i;
	u32 regval;

#if FXGMAC_TX_HANG_TIMER_EN
	pdata->tx_hang_restart_queuing = 0;
#endif

	/* Enable each Tx DMA channel */
#ifndef DPDK
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS,
					     DMA_CH_TCR_ST_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
	}
#else
	PMD_INIT_FUNC_TRACE();
	struct fxgmac_tx_queue *txq;
	struct rte_eth_dev *dev = pdata->expansion.eth_dev;

	for (i = 0; i < dev->data->nb_tx_queues; i++) {
		txq = dev->data->tx_queues[i];
		/* Enable Tx DMA channel */
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, ST, 1);
	}
#endif

	/* Enable each Tx queue */
	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS,
					     MTL_Q_TQOMR_TXQEN_LEN,
					     MTL_Q_ENABLED);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
	}

	/* Enable MAC Tx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);
}

static void fxgmac_disable_tx(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
#endif
	unsigned int i;
	u32 regval;

	/* Prepare for Tx DMA channel stop */
#ifndef DPDK
	channel = pdata->channel_head;
	if (channel != NULL) {
		for (i = 0; i < pdata->channel_count; i++, channel++) {
			if (!channel->tx_ring)
				break;

			fxgmac_prepare_tx_stop(pdata, channel);

#if FXGMAC_TX_HANG_TIMER_EN
			pdata->tx_hang_restart_queuing = 0;
#endif
		}
	}

#else
	PMD_INIT_FUNC_TRACE();
	struct fxgmac_tx_queue *txq;
	struct rte_eth_dev *dev = pdata->expansion.eth_dev;

	for (i = 0; i < pdata->tx_q_count; i++) {
		txq = dev->data->tx_queues[i];
		fxgmac_txq_prepare_tx_stop(pdata, i);
	}
#endif

	/* Disable MAC Tx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	/* Disable each Tx queue */
	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS,
					     MTL_Q_TQOMR_TXQEN_LEN, 0);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
	}

	/* Disable each Tx DMA channel */
#ifndef DPDK
	channel = pdata->channel_head;
	if (channel != NULL) {
		for (i = 0; i < pdata->channel_count; i++, channel++) {
			if (!channel->tx_ring)
				break;

			regval = readreg(pdata->pAdapter,
					 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
			regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS,
						     DMA_CH_TCR_ST_LEN, 0);
			writereg(pdata->pAdapter, regval,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		}
	}
#else
	for (i = 0; i < dev->data->nb_tx_queues; i++) {
		txq = dev->data->tx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, ST, 0);
	}
#endif
}

static void fxgmac_prepare_rx_stop(struct fxgmac_pdata *pdata,
				   unsigned int queue)
{
	unsigned int rx_status, prxq;
	unsigned int rxqsts;
	unsigned long rx_timeout;
	/* The Rx engine cannot be stopped if it is actively processing
	 * packets. Wait for the Rx queue to empty the Rx fifo.  Don't
	 * wait forever though...
	 */
#if FXGMAC_TX_HANG_TIMER_EN
	rx_timeout =
		jiffies + msecs_to_jiffies(500); /* 500ms, larger is better */
#else
	rx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ);
#endif
	while (time_before(jiffies, rx_timeout)) {
		rx_status = readreg(pdata->pAdapter,
				    FXGMAC_MTL_REG(pdata, queue, MTL_Q_RQDR));
		prxq = FXGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_PRXQ_POS,
					   MTL_Q_RQDR_PRXQ_LEN);
		rxqsts = FXGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_RXQSTS_POS,
					     MTL_Q_RQDR_RXQSTS_LEN);
		if ((prxq == 0) && (rxqsts == 0))
			break;

		usleep_range_ex(pdata->pAdapter, 500, 1000);
	}

	if (!time_before(jiffies, rx_timeout))
		netdev_info(pdata->netdev,
			    "timed out waiting for Rx queue %u to empty\n",
			    queue);
}

static void fxgmac_enable_rx(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
#endif
	unsigned int regval, i;

	/* Enable each Rx DMA channel */
#ifndef DPDK
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS,
					     DMA_CH_RCR_SR_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
	}

#else
	PMD_INIT_FUNC_TRACE();
	struct fxgmac_rx_queue *rxq;
	struct rte_eth_dev *dev = pdata->expansion.eth_dev;

	for (i = 0; i < dev->data->nb_rx_queues; i++) {
		rxq = dev->data->rx_queues[i];
		/* Enable Rx DMA channel */
		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, SR, 1);
	}
#endif

	/* Enable each Rx queue */
	regval = 0;
	for (i = 0; i < pdata->rx_q_count; i++)
		regval |= (0x02 << (i << 1));
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RQC0R);

#ifndef DPDK
	/* Enable MAC Rx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, MAC_CR_CST_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);
#else
	/* Enable MAC Rx */
	FXGMAC_IOWRITE_BITS(pdata, MAC_ECR, DCRCC, 1);

	/* Frame is forwarded after stripping CRC to application*/
	if (pdata->expansion.crc_strip_enable) {
		FXGMAC_IOWRITE_BITS(pdata, MAC_CR, CST, 1);
		FXGMAC_IOWRITE_BITS(pdata, MAC_CR, ACS, 1);
	}
	FXGMAC_IOWRITE_BITS(pdata, MAC_CR, RE, 1);
#endif
}
static void fxgmac_enable_channel_rx(struct fxgmac_pdata *pdata,
				     unsigned int queue)
{
	struct fxgmac_channel *channel;
	unsigned int regval;

	/* Enable Rx DMA channel */
	channel = pdata->channel_head + queue;

	if (!channel->rx_ring)
		return;
	regval = readreg(pdata->pAdapter, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
	regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS,
				     DMA_CH_RCR_SR_LEN, 1);
	writereg(pdata->pAdapter, regval, FXGMAC_DMA_REG(channel, DMA_CH_RCR));
	/* Enable  Rx queue */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RQC0R);
	regval |= (0x02 << (queue << 1));
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RQC0R);

	/* Enable MAC Rx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	if (!(regval & ((0x01 << MAC_CR_CST_POS) | (0x01 << MAC_CR_ACS_POS) |
			(0x01 << MAC_CR_RE_POS)))) {
		regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS,
					     MAC_CR_CST_LEN, 1);
		regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS,
					     MAC_CR_ACS_LEN, 1);
		regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS,
					     MAC_CR_RE_LEN, 1);
		writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);
	}
}

static void fxgmac_disable_rx(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
#endif
	unsigned int i;
	u32 regval;

	/* Disable MAC Rx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, MAC_CR_CST_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	/* Prepare for Rx DMA channel stop */
#ifndef DPDK
	for (i = 0; i < pdata->rx_q_count; i++)
		fxgmac_prepare_rx_stop(pdata, i);
#else
	PMD_INIT_FUNC_TRACE();
	struct fxgmac_rx_queue *rxq;
	struct rte_eth_dev *dev = pdata->expansion.eth_dev;

	for (i = 0; i < dev->data->nb_rx_queues; i++) {
		rxq = dev->data->rx_queues[i];
		fxgmac_prepare_rx_stop(pdata, i);
	}
#endif

	/* Disable each Rx queue */
	writereg(pdata->pAdapter, 0, pdata->mac_regs + MAC_RQC0R);

	/* Disable each Rx DMA channel */
#ifndef DPDK
	channel = pdata->channel_head;
	if (channel != NULL) {
		for (i = 0; i < pdata->channel_count; i++, channel++) {
			if (!channel->rx_ring)
				break;

			regval = readreg(pdata->pAdapter,
					 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
			regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS,
						     DMA_CH_RCR_SR_LEN, 0);
			writereg(pdata->pAdapter, regval,
				 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		}
	}
#else
	for (i = 0; i < dev->data->nb_rx_queues; i++) {
		rxq = dev->data->rx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, SR, 0);
	}
#endif
}

static void fxgmac_tx_start_xmit(struct fxgmac_channel *channel,
				 struct fxgmac_ring *ring)
{
	struct fxgmac_pdata *pdata = channel->pdata;
	struct fxgmac_desc_data *desc_data;

	/* Make sure everything is written before the register write */
	wmb();

	/* Issue a poll command to Tx DMA by writing address
	 * of next immediate free descriptor
	 */
	desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur);

#if !(FXGMAC_DUMMY_TX_DEBUG)
	writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_TDTR_LO));
#else
	DPRINTK("dummy tx, fxgmac_tx_start_xmit, tail reg=0x%lx, val=%08x\n",
		FXGMAC_DMA_REG(channel, DMA_CH_TDTR_LO) - pdata->mac_regs,
		(u32)lower_32_bits(desc_data->dma_desc_addr));
#endif
	if (netif_msg_tx_done(pdata))
		DPRINTK("tx_start_xmit: dump before wr reg, dma base=0x%016llx, reg=0x%08x, tx timer usecs=%u, tx_timer_active=%u\n",
			desc_data->dma_desc_addr,
			readreg(pdata->pAdapter,
				FXGMAC_DMA_REG(channel, DMA_CH_TDTR_LO)),
			pdata->tx_usecs, channel->tx_timer_active);

	ring->tx.xmit_more = 0;
}

static void fxgmac_dev_xmit(struct fxgmac_channel *channel)
{
	struct fxgmac_pdata *pdata = channel->pdata;
	struct fxgmac_ring *ring = channel->tx_ring;
	unsigned int tso_context, vlan_context;
	struct fxgmac_desc_data *desc_data;
	struct fxgmac_dma_desc *dma_desc;
	struct fxgmac_pkt_info *pkt_info;
	unsigned int csum, tso, vlan;
	int start_index = ring->cur;
	int cur_index = ring->cur;
	int i;

	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit callin, desc cur=%d\n", cur_index);

	pkt_info = &ring->pkt_info;
	csum = FXGMAC_GET_REG_BITS(pkt_info->attributes,
				   TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS,
				   TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN);
	tso = FXGMAC_GET_REG_BITS(pkt_info->attributes,
				  TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
				  TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN);
	vlan = FXGMAC_GET_REG_BITS(pkt_info->attributes,
				   TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
				   TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN);

	if (tso && (pkt_info->mss != ring->tx.cur_mss))
		tso_context = 1;
	else
		tso_context = 0;

	if ((tso_context) && (netif_msg_tx_done(pdata))) {
		/* tso is initialized to start... */
		DPRINTK("fxgmac_dev_xmit, tso_%s tso=0x%x, pkt_mss=%d, cur_mss=%d\n",
			(pkt_info->mss) ? "start" : "stop", tso, pkt_info->mss,
			ring->tx.cur_mss);
	}

	if (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag))
		vlan_context = 1;
	else
		vlan_context = 0;

	if (vlan && (netif_msg_tx_done(pdata)))
		DPRINTK("fxgmac_dev_xmi:pkt vlan=%d, ring vlan=%d, vlan_context=%d\n",
			pkt_info->vlan_ctag, ring->tx.cur_vlan_ctag,
			vlan_context);

	desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index);
	dma_desc = desc_data->dma_desc;

	/* Create a context descriptor if this is a TSO pkt_info */
	if (tso_context || vlan_context) {
		if (tso_context) {
			if (netif_msg_tx_done(pdata))
				DPRINTK("xlgamc dev xmit, construct tso context descriptor, mss=%u\n",
					pkt_info->mss);

			/* Set the MSS size */
			dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc2, TX_CONTEXT_DESC2_MSS_POS,
				TX_CONTEXT_DESC2_MSS_LEN, pkt_info->mss);

			/* Mark it as a CONTEXT descriptor */
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_CONTEXT_DESC3_CTXT_POS,
				TX_CONTEXT_DESC3_CTXT_LEN, 1);

			/* Indicate this descriptor contains the MSS */
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_CONTEXT_DESC3_TCMSSV_POS,
				TX_CONTEXT_DESC3_TCMSSV_LEN, 1);

			ring->tx.cur_mss = pkt_info->mss;
		}

		if (vlan_context) {
			netif_dbg(pdata, tx_queued, pdata->netdev,
				  "VLAN context descriptor, ctag=%u\n",
				  pkt_info->vlan_ctag);

			/* Mark it as a CONTEXT descriptor */
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_CONTEXT_DESC3_CTXT_POS,
				TX_CONTEXT_DESC3_CTXT_LEN, 1);

			/* Set the VLAN tag */
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_CONTEXT_DESC3_VT_POS,
				TX_CONTEXT_DESC3_VT_LEN, pkt_info->vlan_ctag);

			/* Indicate this descriptor contains the VLAN tag */
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_CONTEXT_DESC3_VLTV_POS,
				TX_CONTEXT_DESC3_VLTV_LEN, 1);

			ring->tx.cur_vlan_ctag = pkt_info->vlan_ctag;
		}

		cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count);
		desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index);
		dma_desc = desc_data->dma_desc;
	}

	/* Update buffer address (for TSO this is the header) */
	dma_desc->desc0 = cpu_to_le32(lower_32_bits(desc_data->skb_dma));
	dma_desc->desc1 = cpu_to_le32(upper_32_bits(desc_data->skb_dma));

	/* Update the buffer length */
	dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc2,
						 TX_NORMAL_DESC2_HL_B1L_POS,
						 TX_NORMAL_DESC2_HL_B1L_LEN,
						 desc_data->skb_dma_len);

	/* VLAN tag insertion check */
	if (vlan) {
		dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc2, TX_NORMAL_DESC2_VTIR_POS,
			TX_NORMAL_DESC2_VTIR_LEN, TX_NORMAL_DESC2_VLAN_INSERT);
		pdata->stats.tx_vlan_packets++;
	}

	/* Timestamp enablement check */
	if (FXGMAC_GET_REG_BITS(pkt_info->attributes,
				TX_PACKET_ATTRIBUTES_PTP_POS,
				TX_PACKET_ATTRIBUTES_PTP_LEN))
		dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc2, TX_NORMAL_DESC2_TTSE_POS,
			TX_NORMAL_DESC2_TTSE_LEN, 1);

	/* Mark it as First Descriptor */
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 TX_NORMAL_DESC3_FD_POS,
						 TX_NORMAL_DESC3_FD_LEN, 1);

	/* Mark it as a NORMAL descriptor */
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 TX_NORMAL_DESC3_CTXT_POS,
						 TX_NORMAL_DESC3_CTXT_LEN, 0);

	/* Set OWN bit if not the first descriptor */
	if (cur_index != start_index)
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS,
			TX_NORMAL_DESC3_OWN_LEN, 1);

	if (tso) {
		/* Enable TSO */
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_TSE_POS,
			TX_NORMAL_DESC3_TSE_LEN, 1);
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_TCPPL_POS,
			TX_NORMAL_DESC3_TCPPL_LEN, pkt_info->tcp_payload_len);
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_TCPHDRLEN_POS,
			TX_NORMAL_DESC3_TCPHDRLEN_LEN,
			pkt_info->tcp_header_len / 4);

		pdata->stats.tx_tso_packets++;
	} else {
		/* Enable CRC and Pad Insertion */
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_CPC_POS,
			TX_NORMAL_DESC3_CPC_LEN, 0);

		/* Enable HW CSUM */
		if (csum)
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_NORMAL_DESC3_CIC_POS,
				TX_NORMAL_DESC3_CIC_LEN, 0x3);

		/* Set the total length to be transmitted */
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
							 TX_NORMAL_DESC3_FL_POS,
							 TX_NORMAL_DESC3_FL_LEN,
							 pkt_info->length);
	}
	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit before more descs, desc cur=%d, start=%d, desc=%#x,%#x,%#x,%#x\n",
			cur_index, start_index, dma_desc->desc0,
			dma_desc->desc1, dma_desc->desc2, dma_desc->desc3);

	if (start_index <= cur_index)
		i = cur_index - start_index + 1;
	else
		i = ring->dma_desc_count - start_index + cur_index;

	for (; i < pkt_info->desc_count; i++) {
		cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count);

		desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index);
		dma_desc = desc_data->dma_desc;

		/* Update buffer address */
		dma_desc->desc0 =
			cpu_to_le32(lower_32_bits(desc_data->skb_dma));
		dma_desc->desc1 =
			cpu_to_le32(upper_32_bits(desc_data->skb_dma));

		/* Update the buffer length */
		dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc2, TX_NORMAL_DESC2_HL_B1L_POS,
			TX_NORMAL_DESC2_HL_B1L_LEN, desc_data->skb_dma_len);

		/* Set OWN bit */
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS,
			TX_NORMAL_DESC3_OWN_LEN, 1);

		/* Mark it as NORMAL descriptor */
		dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
			dma_desc->desc3, TX_NORMAL_DESC3_CTXT_POS,
			TX_NORMAL_DESC3_CTXT_LEN, 0);

		/* Enable HW CSUM */
		if (csum)
			dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(
				dma_desc->desc3, TX_NORMAL_DESC3_CIC_POS,
				TX_NORMAL_DESC3_CIC_LEN, 0x3);
	}

	/* Set LAST bit for the last descriptor */
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 TX_NORMAL_DESC3_LD_POS,
						 TX_NORMAL_DESC3_LD_LEN, 1);

	dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc2,
						 TX_NORMAL_DESC2_IC_POS,
						 TX_NORMAL_DESC2_IC_LEN, 1);

	/* Save the Tx info to report back during cleanup */
	desc_data->tx.packets = pkt_info->tx_packets;
	desc_data->tx.bytes = pkt_info->tx_bytes;

	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit last descs, desc cur=%d, desc=%#x,%#x,%#x,%#x\n",
			cur_index, dma_desc->desc0, dma_desc->desc1,
			dma_desc->desc2, dma_desc->desc3);

	/* In case the Tx DMA engine is running, make sure everything
	 * is written to the descriptor(s) before setting the OWN bit
	 * for the first descriptor
	 */
	dma_wmb();

	/* Set OWN bit for the first descriptor */
	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index);
	dma_desc = desc_data->dma_desc;
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 TX_NORMAL_DESC3_OWN_POS,
						 TX_NORMAL_DESC3_OWN_LEN, 1);

	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit first descs, start=%d, desc=%#x,%#x,%#x,%#x\n",
			start_index, dma_desc->desc0, dma_desc->desc1,
			dma_desc->desc2, dma_desc->desc3);

	if (netif_msg_tx_queued(pdata))
		fxgmac_dump_tx_desc(pdata, ring, start_index,
				    pkt_info->desc_count, 1);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit about to call tx_start_xmit, ring xmit_more=%d, txq_stopped=%x\n",
			ring->tx.xmit_more,
			netif_xmit_stopped(netdev_get_tx_queue(
				pdata->netdev, channel->queue_index)));
#else /* ( LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,165))*/
	if (netif_msg_tx_done(pdata))
		DPRINTK("dev_xmit about to call tx_start_xmit, pkt xmit_more=%d, txq_stopped=%x\n",
			pkt_info->skb->xmit_more,
			netif_xmit_stopped(netdev_get_tx_queue(
				pdata->netdev, channel->queue_index)));
#endif

	/* Make sure ownership is written to the descriptor */
	smp_wmb();

	ring->cur = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count);

	fxgmac_tx_start_xmit(channel, ring);

	/* yzhang for reduce debug output */
	if (netif_msg_tx_done(pdata)) {
		DPRINTK("dev_xmit callout %s: descriptors %u to %u written\n",
			channel->name, start_index & (ring->dma_desc_count - 1),
			(ring->cur - 1) & (ring->dma_desc_count - 1));
	}
}

static void fxgmac_get_rx_tstamp(struct fxgmac_pkt_info *pkt_info,
				 struct fxgmac_dma_desc *dma_desc)
{
	u64 nsec;

	nsec = le32_to_cpu(dma_desc->desc1);
	nsec <<= 32;
	nsec |= le32_to_cpu(dma_desc->desc0);
	if (nsec != 0xffffffffffffffffULL) {
		pkt_info->rx_tstamp = nsec;
		pkt_info->attributes = FXGMAC_SET_REG_BITS(
			pkt_info->attributes,
			RX_PACKET_ATTRIBUTES_RX_TSTAMP_POS,
			RX_PACKET_ATTRIBUTES_RX_TSTAMP_LEN, 1);
	}
}

static void fxgmac_tx_desc_reset(struct fxgmac_desc_data *desc_data)
{
	struct fxgmac_dma_desc *dma_desc = desc_data->dma_desc;

	/* Reset the Tx descriptor
	 *   Set buffer 1 (lo) address to zero
	 *   Set buffer 1 (hi) address to zero
	 *   Reset all other control bits (IC, TTSE, B2L & B1L)
	 *   Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc)
	 */
	dma_desc->desc0 = 0;
	dma_desc->desc1 = 0;
	dma_desc->desc2 = 0;
	dma_desc->desc3 = 0;

	/* Make sure ownership is written to the descriptor */
	dma_wmb();
}

static void fxgmac_tx_desc_init(struct fxgmac_channel *channel)
{
	struct fxgmac_ring *ring = channel->tx_ring;
	struct fxgmac_desc_data *desc_data;
	int start_index = ring->cur;
	unsigned int i;
	start_index = start_index;

	/* Initialize all descriptors */
	for (i = 0; i < ring->dma_desc_count; i++) {
		desc_data = FXGMAC_GET_DESC_DATA(ring, i);

		/* Initialize Tx descriptor */
		fxgmac_tx_desc_reset(desc_data);
	}

	writereg(channel->pdata->pAdapter, channel->pdata->tx_desc_count - 1,
		 FXGMAC_DMA_REG(channel, DMA_CH_TDRLR));

	/* Update the starting address of descriptor ring */
	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index);
	writereg(channel->pdata->pAdapter,
		 upper_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_TDLR_HI));
	writereg(channel->pdata->pAdapter,
		 lower_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_TDLR_LO));
}

static void fxgmac_rx_desc_reset(struct fxgmac_pdata *pdata,
				 struct fxgmac_desc_data *desc_data,
				 unsigned int index)
{
	struct fxgmac_dma_desc *dma_desc = desc_data->dma_desc;

	/* Reset the Rx descriptor
	 * Set buffer 1 (lo) address to header dma address (lo)
	 * Set buffer 1 (hi) address to header dma address (hi)
	 * Set buffer 2 (lo) address to buffer dma address (lo)
	 * Set buffer 2 (hi) address to buffer dma address (hi) and
	 *   set control bits OWN and INTE
	 */
	dma_desc->desc0 =
		cpu_to_le32(lower_32_bits(desc_data->rx.buf.dma_base));
	dma_desc->desc1 =
		cpu_to_le32(upper_32_bits(desc_data->rx.buf.dma_base));
	dma_desc->desc2 = 0;
	dma_desc->desc3 = 0;
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 RX_NORMAL_DESC3_INTE_POS,
						 RX_NORMAL_DESC3_INTE_LEN, 1);
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 RX_NORMAL_DESC3_BUF2V_POS,
						 RX_NORMAL_DESC3_BUF2V_LEN, 0);
	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 RX_NORMAL_DESC3_BUF1V_POS,
						 RX_NORMAL_DESC3_BUF1V_LEN, 1);

	/* Since the Rx DMA engine is likely running, make sure everything
	 * is written to the descriptor(s) before setting the OWN bit
	 * for the descriptor
	 */
	dma_wmb();

	dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3,
						 RX_NORMAL_DESC3_OWN_POS,
						 RX_NORMAL_DESC3_OWN_LEN, 1);

	/* Make sure ownership is written to the descriptor */
	dma_wmb();
}

static void fxgmac_rx_desc_init(struct fxgmac_channel *channel)
{
	struct fxgmac_pdata *pdata = channel->pdata;
	struct fxgmac_ring *ring = channel->rx_ring;
	unsigned int start_index = ring->cur;
	struct fxgmac_desc_data *desc_data;
	unsigned int i;

	/* Initialize all descriptors */
	for (i = 0; i < ring->dma_desc_count; i++) {
		desc_data = FXGMAC_GET_DESC_DATA(ring, i);

		/* Initialize Rx descriptor */
		fxgmac_rx_desc_reset(pdata, desc_data, i);
	}

	/* Update the total number of Rx descriptors */
	writereg(pdata->pAdapter, ring->dma_desc_count - 1,
		 FXGMAC_DMA_REG(channel, DMA_CH_RDRLR));

	/* Update the starting address of descriptor ring */
	desc_data = FXGMAC_GET_DESC_DATA(ring, start_index);
	writereg(pdata->pAdapter, upper_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_RDLR_HI));
	writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_RDLR_LO));

	/* Update the Rx Descriptor Tail Pointer */
	desc_data = FXGMAC_GET_DESC_DATA(
		ring, start_index + ring->dma_desc_count - 1);
	writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr),
		 FXGMAC_DMA_REG(channel, DMA_CH_RDTR_LO));
}

static int fxgmac_is_context_desc(struct fxgmac_dma_desc *dma_desc)
{
	/* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */
	return FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, TX_NORMAL_DESC3_CTXT_POS,
				      TX_NORMAL_DESC3_CTXT_LEN);
}

static int fxgmac_is_last_desc(struct fxgmac_dma_desc *dma_desc)
{
	/* Rx and Tx share LD bit, so check TDES3.LD bit */
	return FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, TX_NORMAL_DESC3_LD_POS,
				      TX_NORMAL_DESC3_LD_LEN);
}

static int fxgmac_disable_tx_flow_control(struct fxgmac_pdata *pdata)
{
	unsigned int max_q_count, q_count;
	unsigned int reg, regval;
	unsigned int i;

	/* Clear MTL flow control */
	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS,
					     MTL_Q_RQOMR_EHFC_LEN, 0);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}

	/* Clear MAC flow control */
	max_q_count = FXGMAC_MAX_FLOW_CONTROL_QUEUES;
	q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count);
	reg = MAC_Q0TFCR;
	for (i = 0; i < q_count; i++) {
		regval = readreg(pdata->pAdapter, pdata->mac_regs + reg);
		regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_TFE_POS,
					     MAC_Q0TFCR_TFE_LEN, 0);
		writereg(pdata->pAdapter, regval, pdata->mac_regs + reg);

		reg += MAC_QTFCR_INC;
	}

	return 0;
}

static int fxgmac_enable_tx_flow_control(struct fxgmac_pdata *pdata)
{
	unsigned int max_q_count, q_count;
	unsigned int reg, regval;
	unsigned int i;

	/* Set MTL flow control */
	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS,
					     MTL_Q_RQOMR_EHFC_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}

	/* Set MAC flow control */
	max_q_count = FXGMAC_MAX_FLOW_CONTROL_QUEUES;
	q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count);
	reg = MAC_Q0TFCR;
	for (i = 0; i < q_count; i++) {
		regval = readreg(pdata->pAdapter, pdata->mac_regs + reg);

		/* Enable transmit flow control */
		regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_TFE_POS,
					     MAC_Q0TFCR_TFE_LEN, 1);
		/* Set pause time */
		regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_PT_POS,
					     MAC_Q0TFCR_PT_LEN, 0xffff);

		writereg(pdata->pAdapter, regval, pdata->mac_regs + reg);

		reg += MAC_QTFCR_INC;
	}

	return 0;
}

static int fxgmac_disable_rx_flow_control(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RFCR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, MAC_RFCR_RFE_LEN,
				     0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RFCR);

	return 0;
}

static int fxgmac_enable_rx_flow_control(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RFCR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, MAC_RFCR_RFE_LEN,
				     1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RFCR);

	return 0;
}

static int fxgmac_config_tx_flow_control(struct fxgmac_pdata *pdata)
{
	if (pdata->tx_pause)
		fxgmac_enable_tx_flow_control(pdata);
	else
		fxgmac_disable_tx_flow_control(pdata);

	return 0;
}

static int fxgmac_config_rx_flow_control(struct fxgmac_pdata *pdata)
{
	if (pdata->rx_pause)
		fxgmac_enable_rx_flow_control(pdata);
	else
		fxgmac_disable_rx_flow_control(pdata);

	return 0;
}

static int fxgmac_config_rx_coalesce(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->rx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_RIWT));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RIWT_RWT_POS,
					     DMA_CH_RIWT_RWT_LEN,
					     pdata->rx_riwt);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_RIWT));
	}
#else
	struct fxgmac_rx_queue *rxq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) {
		rxq = pdata->expansion.eth_dev->data->rx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RIWT, RWT, pdata->rx_riwt);
	}
#endif

	return 0;
}

static void fxgmac_config_rx_fep_disable(struct fxgmac_pdata *pdata)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		/* 1:enable  the  rx queue forward packet with error
		 * status(crc error, gmii_er, watch dog timeout.or overflow)
		 */
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FEP_POS,
					     MTL_Q_RQOMR_FEP_LEN,
					     MTL_FEP_ENABLE);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}
}

static void fxgmac_config_rx_fup_enable(struct fxgmac_pdata *pdata)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FUP_POS,
					     MTL_Q_RQOMR_FUP_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}
}

static int fxgmac_config_tx_coalesce(struct fxgmac_pdata *pdata)
{
	pdata = pdata;
	return 0;
}

static void fxgmac_config_rx_buffer_size(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->rx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_RBSZ_POS,
					     DMA_CH_RCR_RBSZ_LEN,
					     pdata->rx_buf_size);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
	}
#else
	struct fxgmac_rx_queue *rxq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) {
		rxq = pdata->expansion.eth_dev->data->rx_queues[i];

		rxq->buf_size = rte_pktmbuf_data_room_size(rxq->mb_pool) -
				RTE_PKTMBUF_HEADROOM;
		rxq->buf_size = (rxq->buf_size + FXGMAC_RX_BUF_ALIGN - 1) &
				~(FXGMAC_RX_BUF_ALIGN - 1);

		if (rxq->buf_size > pdata->rx_buf_size)
			pdata->rx_buf_size = rxq->buf_size;

		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, RBSZ, rxq->buf_size);
	}
#endif
}

static void fxgmac_config_tso_mode(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->tx_ring)
			break;

		if (pdata->hw_feat.tso) {
			regval = readreg(pdata->pAdapter,
					 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
			regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_TSE_POS,
						     DMA_CH_TCR_TSE_LEN, 1);
			writereg(pdata->pAdapter, regval,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		}
	}
#else
	struct fxgmac_tx_queue *txq;
	unsigned int i;
	for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) {
		txq = pdata->expansion.eth_dev->data->tx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, TSE, pdata->tx_pbl);
	}
#endif
}

static void fxgmac_config_sph_mode(struct fxgmac_pdata *pdata)
{
	unsigned int i;
	u32 regval;

#ifndef DPDK
	struct fxgmac_channel *channel;
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->rx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_CR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_CR_SPH_POS,
					     DMA_CH_CR_SPH_LEN, 0);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_CR));
	}
#else
	struct fxgmac_rx_queue *rxq;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) {
		rxq = pdata->expansion.eth_dev->data->rx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_CR, SPH, pdata->rx_pbl);
	}
#endif

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ECR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_ECR_HDSMS_POS,
				     MAC_ECR_HDSMS_LEN, FXGMAC_SPH_HDSMS_SIZE);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_ECR);
}

static unsigned int fxgmac_usec_to_riwt(struct fxgmac_pdata *pdata,
					unsigned int usec)
{
	unsigned long rate;
	unsigned int ret;

	rate = pdata->sysclk_rate;

	/* Convert the input usec value to the watchdog timer value. Each
	 * watchdog timer value is equivalent to 256 clock cycles.
	 * Calculate the required value as:
	 *   ( usec * ( system_clock_mhz / 10^6) / 256
	 */
	ret = (usec * (rate / 1000000)) / 256;

	return ret;
}

static unsigned int fxgmac_riwt_to_usec(struct fxgmac_pdata *pdata,
					unsigned int riwt)
{
	unsigned long rate;
	unsigned int ret;

	rate = pdata->sysclk_rate;

	/* Convert the input watchdog timer value to the usec value. Each
	 * watchdog timer value is equivalent to 256 clock cycles.
	 * Calculate the required value as:
	 *   ( riwt * 256) / ( system_clock_mhz / 10^6)
	 */
	ret = (riwt * 256) / (rate / 1000000);

	return ret;
}

static int fxgmac_config_rx_threshold(struct fxgmac_pdata *pdata,
				      unsigned int val)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RTC_POS,
					     MTL_Q_RQOMR_RTC_LEN, val);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}

	return 0;
}

static void fxgmac_config_mtl_mode(struct fxgmac_pdata *pdata)
{
	unsigned int i;
	u32 regval;

	/* Set Tx to weighted round robin scheduling algorithm */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MTL_OMR);
	regval = FXGMAC_SET_REG_BITS(regval, MTL_OMR_ETSALG_POS,
				     MTL_OMR_ETSALG_LEN, MTL_ETSALG_WRR);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MTL_OMR);

	/* Set Tx traffic classes to use WRR algorithm with equal weights */
	for (i = 0; i < pdata->tx_q_count /*hw_feat.tc_cnt*/; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_TC_QWR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_TC_QWR_QW_POS,
					     MTL_TC_QWR_QW_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_TC_QWR));
	}

	/* Set Rx to strict priority algorithm */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MTL_OMR);
	regval = FXGMAC_SET_REG_BITS(regval, MTL_OMR_RAA_POS, MTL_OMR_RAA_LEN,
				     MTL_RAA_SP);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MTL_OMR);
}

static void fxgmac_config_queue_mapping(struct fxgmac_pdata *pdata)
{
	unsigned int ppq, ppq_extra, prio, prio_queues;
	unsigned int queue;
	unsigned int reg, regval;
	unsigned int mask;
	unsigned int i, j;

	/* Map the MTL Tx Queues to Traffic Classes
	 * Note: Tx Queues >= Traffic Classes
	 */
	queue = 0;
	DPRINTK("need to map TXq(%u) to TC\n", queue);

	/* Map the 8 VLAN priority values to available MTL Rx queues */
	prio_queues =
		min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, pdata->rx_q_count);
	ppq = IEEE_8021QAZ_MAX_TCS / prio_queues;
	ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues;

	reg = MAC_RQC2R;
	regval = 0;
	for (i = 0, prio = 0; i < prio_queues;) {
		mask = 0;
		for (j = 0; j < ppq; j++) {
			netif_dbg(pdata, drv, pdata->netdev,
				  "PRIO%u mapped to RXq%u\n", prio, i);
			mask |= (1 << prio);
			prio++;
		}

		if (i < ppq_extra) {
			netif_dbg(pdata, drv, pdata->netdev,
				  "PRIO%u mapped to RXq%u\n", prio, i);
			mask |= (1 << prio);
			prio++;
		}

		regval |= (mask << ((i++ % MAC_RQC2_Q_PER_REG) << 3));

		if ((i % MAC_RQC2_Q_PER_REG) && (i != prio_queues))
			continue;

		writereg(pdata->pAdapter, regval, pdata->mac_regs + reg);
		reg += MAC_RQC2_INC;
		regval = 0;
	}

	/* Configure one to one, MTL Rx queue to DMA Rx channel mapping
	 * ie Q0 <--> CH0, Q1 <--> CH1 ... Q11 <--> CH11
	 */
	reg = MTL_RQDCM0R;
	regval = readreg(pdata->pAdapter, pdata->mac_regs + reg);
	regval |= (MTL_RQDCM0R_Q0MDMACH | MTL_RQDCM0R_Q1MDMACH |
		   MTL_RQDCM0R_Q2MDMACH | MTL_RQDCM0R_Q3MDMACH);

	if (pdata->rss) {
		/* in version later 0617, need to enable DA-based DMA Channel Selection to let RSS work,
		 * ie, bit4,12,20,28 for Q0,1,2,3 individual
		 */
		regval |= (MTL_RQDCM0R_Q0DDMACH | MTL_RQDCM0R_Q1DDMACH |
			   MTL_RQDCM0R_Q2DDMACH | MTL_RQDCM0R_Q3DDMACH);
	}

	writereg(pdata->pAdapter, regval, pdata->mac_regs + reg);

	reg += MTL_RQDCM_INC;
	regval = readreg(pdata->pAdapter, pdata->mac_regs + reg);
	regval |= (MTL_RQDCM1R_Q4MDMACH | MTL_RQDCM1R_Q5MDMACH |
		   MTL_RQDCM1R_Q6MDMACH | MTL_RQDCM1R_Q7MDMACH);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + reg);
}

static unsigned int fxgmac_calculate_per_queue_fifo(unsigned int fifo_size,
						    unsigned int queue_count)
{
	unsigned int q_fifo_size;
	unsigned int p_fifo;

	/* Calculate the configured fifo size */
	q_fifo_size = 1 << (fifo_size + 7);

	/* The configured value may not be the actual amount of fifo RAM */
	q_fifo_size = min_t(unsigned int, FXGMAC_MAX_FIFO, q_fifo_size);

	q_fifo_size = q_fifo_size / queue_count;

	/* Each increment in the queue fifo size represents 256 bytes of
	 * fifo, with 0 representing 256 bytes. Distribute the fifo equally
	 * between the queues.
	 */
	p_fifo = q_fifo_size / 256;
	if (p_fifo)
		p_fifo--;

	return p_fifo;
}

static void fxgmac_config_tx_fifo_size(struct fxgmac_pdata *pdata)
{
	unsigned int fifo_size;
	unsigned int i;
	u32 regval;

	fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size,
						    pdata->tx_q_count);

	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TQS_POS,
					     MTL_Q_TQOMR_TQS_LEN, fifo_size);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
	}

	netif_info(pdata, drv, pdata->netdev,
		   "%d Tx hardware queues, %d byte fifo per queue\n",
		   pdata->tx_q_count, ((fifo_size + 1) * 256));
}

static void fxgmac_config_rx_fifo_size(struct fxgmac_pdata *pdata)
{
	unsigned int fifo_size;
	unsigned int i;
	u32 regval;

	fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size,
						    pdata->rx_q_count);

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RQS_POS,
					     MTL_Q_RQOMR_RQS_LEN, fifo_size);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}

	netif_info(pdata, drv, pdata->netdev,
		   "%d Rx hardware queues, %d byte fifo per queue\n",
		   pdata->rx_q_count, ((fifo_size + 1) * 256));
}

static void fxgmac_config_flow_control_threshold(struct fxgmac_pdata *pdata)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		/* Activate flow control when less than 6k left in fifo */
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RFA_POS,
					     MTL_Q_RQOMR_RFA_LEN, 6);
		/* De-activate flow control when more than 10k left in fifo */
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RFD_POS,
					     MTL_Q_RQOMR_RFD_LEN, 10);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}
}

static int fxgmac_config_tx_threshold(struct fxgmac_pdata *pdata,
				      unsigned int val)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TTC_POS,
					     MTL_Q_TQOMR_TTC_LEN, val);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
	}

	return 0;
}

static int fxgmac_config_rsf_mode(struct fxgmac_pdata *pdata, unsigned int val)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->rx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RSF_POS,
					     MTL_Q_RQOMR_RSF_LEN, val);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR));
	}

	return 0;
}

static int fxgmac_config_tsf_mode(struct fxgmac_pdata *pdata, unsigned int val)
{
	unsigned int i;
	u32 regval;

	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TSF_POS,
					     MTL_Q_TQOMR_TSF_LEN, val);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
	}

	return 0;
}

static int fxgmac_config_osp_mode(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->tx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_OSP_POS,
					     DMA_CH_TCR_OSP_LEN,
					     pdata->tx_osp_mode);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
	}
#else
	/* Force DMA to operate on second packet before closing descriptors
	 *  of first packet
	 */
	struct fxgmac_tx_queue *txq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) {
		txq = pdata->expansion.eth_dev->data->tx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, OSP,
					pdata->tx_osp_mode);
	}
#endif
	return 0;
}

static int fxgmac_config_pblx8(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_CR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_CR_PBLX8_POS,
					     DMA_CH_CR_PBLX8_LEN, pdata->pblx8);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_CR));
	}
#else
	struct fxgmac_tx_queue *txq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) {
		txq = pdata->expansion.eth_dev->data->tx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_CR, PBLX8, pdata->pblx8);
	}
#endif

	return 0;
}

static int fxgmac_get_tx_pbl_val(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter,
			 FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_TCR));
	regval = FXGMAC_GET_REG_BITS(regval, DMA_CH_TCR_PBL_POS,
				     DMA_CH_TCR_PBL_LEN);
	return regval;
}

static int fxgmac_config_tx_pbl_val(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->tx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_PBL_POS,
					     DMA_CH_TCR_PBL_LEN, pdata->tx_pbl);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
	}
#else
	struct fxgmac_tx_queue *txq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) {
		txq = pdata->expansion.eth_dev->data->tx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, PBL, pdata->tx_pbl);
	}
#endif

	return 0;
}

static int fxgmac_get_rx_pbl_val(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter,
			 FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_RCR));
	regval = FXGMAC_GET_REG_BITS(regval, DMA_CH_RCR_PBL_POS,
				     DMA_CH_RCR_PBL_LEN);
	return regval;
}

static int fxgmac_config_rx_pbl_val(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->rx_ring)
			break;

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_PBL_POS,
					     DMA_CH_RCR_PBL_LEN, pdata->rx_pbl);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
	}
#else
	struct fxgmac_rx_queue *rxq;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) {
		rxq = pdata->expansion.eth_dev->data->rx_queues[i];
		FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, PBL, pdata->rx_pbl);
	}
#endif

	return 0;
}

static u64 fxgmac_mmc_read(struct fxgmac_pdata *pdata, unsigned int reg_lo)
{
	/* bool read_hi; */
	u64 val;
	val = (u64)readreg(pdata->pAdapter, pdata->mac_regs + reg_lo);

	return val;
}

static void fxgmac_tx_mmc_int(struct fxgmac_pdata *pdata)
{
	unsigned int mmc_isr =
		readreg(pdata->pAdapter, pdata->mac_regs + MMC_TISR);
	struct fxgmac_stats *stats = &pdata->stats;

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOCTETCOUNT_GB_POS,
				MMC_TISR_TXOCTETCOUNT_GB_LEN))
		stats->txoctetcount_gb +=
			fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXFRAMECOUNT_GB_POS,
				MMC_TISR_TXFRAMECOUNT_GB_LEN))
		stats->txframecount_gb +=
			fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXBROADCASTFRAMES_G_POS,
				MMC_TISR_TXBROADCASTFRAMES_G_LEN))
		stats->txbroadcastframes_g +=
			fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTICASTFRAMES_G_POS,
				MMC_TISR_TXMULTICASTFRAMES_G_LEN))
		stats->txmulticastframes_g +=
			fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX64OCTETS_GB_POS,
				MMC_TISR_TX64OCTETS_GB_LEN))
		stats->tx64octets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX65TO127OCTETS_GB_POS,
				MMC_TISR_TX65TO127OCTETS_GB_LEN))
		stats->tx65to127octets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX128TO255OCTETS_GB_POS,
				MMC_TISR_TX128TO255OCTETS_GB_LEN))
		stats->tx128to255octets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX256TO511OCTETS_GB_POS,
				MMC_TISR_TX256TO511OCTETS_GB_LEN))
		stats->tx256to511octets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX512TO1023OCTETS_GB_POS,
				MMC_TISR_TX512TO1023OCTETS_GB_LEN))
		stats->tx512to1023octets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX1024TOMAXOCTETS_GB_POS,
				MMC_TISR_TX1024TOMAXOCTETS_GB_LEN))
		stats->tx1024tomaxoctets_gb +=
			fxgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXUNICASTFRAMES_GB_POS,
				MMC_TISR_TXUNICASTFRAMES_GB_LEN))
		stats->txunicastframes_gb +=
			fxgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTICASTFRAMES_GB_POS,
				MMC_TISR_TXMULTICASTFRAMES_GB_LEN))
		stats->txmulticastframes_gb +=
			fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXBROADCASTFRAMES_GB_POS,
				MMC_TISR_TXBROADCASTFRAMES_GB_LEN))
		stats->txbroadcastframes_g +=
			fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXUNDERFLOWERROR_POS,
				MMC_TISR_TXUNDERFLOWERROR_LEN))
		stats->txunderflowerror +=
			fxgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXSINGLECOLLISION_G_POS,
				MMC_TISR_TXSINGLECOLLISION_G_LEN))
		stats->txsinglecollision_g +=
			fxgmac_mmc_read(pdata, MMC_TXSINGLECOLLISION_G);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTIPLECOLLISION_G_POS,
				MMC_TISR_TXMULTIPLECOLLISION_G_LEN))
		stats->txmultiplecollision_g +=
			fxgmac_mmc_read(pdata, MMC_TXMULTIPLECOLLISION_G);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXDEFERREDFRAMES_POS,
				MMC_TISR_TXDEFERREDFRAMES_LEN))
		stats->txdeferredframes +=
			fxgmac_mmc_read(pdata, MMC_TXDEFERREDFRAMES);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXLATECOLLISIONFRAMES_POS,
				MMC_TISR_TXLATECOLLISIONFRAMES_LEN))
		stats->txlatecollisionframes +=
			fxgmac_mmc_read(pdata, MMC_TXLATECOLLISIONFRAMES);

	if (FXGMAC_GET_REG_BITS(mmc_isr,
				MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_POS,
				MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_LEN))
		stats->txexcessivecollisionframes +=
			fxgmac_mmc_read(pdata, MMC_TXEXCESSIVECOLLSIONFRAMES);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXCARRIERERRORFRAMES_POS,
				MMC_TISR_TXCARRIERERRORFRAMES_LEN))
		stats->txcarriererrorframes +=
			fxgmac_mmc_read(pdata, MMC_TXCARRIERERRORFRAMES);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOCTETCOUNT_G_POS,
				MMC_TISR_TXOCTETCOUNT_G_LEN))
		stats->txoctetcount_g +=
			fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXFRAMECOUNT_G_POS,
				MMC_TISR_TXFRAMECOUNT_G_LEN))
		stats->txframecount_g +=
			fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_POS,
				MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_LEN))
		stats->txexcessivedeferralerror +=
			fxgmac_mmc_read(pdata, MMC_TXEXCESSIVEDEFERRALERROR);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXPAUSEFRAMES_POS,
				MMC_TISR_TXPAUSEFRAMES_LEN))
		stats->txpauseframes +=
			fxgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXVLANFRAMES_G_POS,
				MMC_TISR_TXVLANFRAMES_G_LEN))
		stats->txvlanframes_g +=
			fxgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOVERSIZE_G_POS,
				MMC_TISR_TXOVERSIZE_G_LEN))
		stats->txoversize_g +=
			fxgmac_mmc_read(pdata, MMC_TXOVERSIZEFRAMES);
}

static void fxgmac_rx_mmc_int(struct fxgmac_pdata *pdata)
{
	unsigned int mmc_isr =
		readreg(pdata->pAdapter, pdata->mac_regs + MMC_RISR);
	struct fxgmac_stats *stats = &pdata->stats;

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXFRAMECOUNT_GB_POS,
				MMC_RISR_RXFRAMECOUNT_GB_LEN))
		stats->rxframecount_gb +=
			fxgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOCTETCOUNT_GB_POS,
				MMC_RISR_RXOCTETCOUNT_GB_LEN))
		stats->rxoctetcount_gb +=
			fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOCTETCOUNT_G_POS,
				MMC_RISR_RXOCTETCOUNT_G_LEN))
		stats->rxoctetcount_g +=
			fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXBROADCASTFRAMES_G_POS,
				MMC_RISR_RXBROADCASTFRAMES_G_LEN))
		stats->rxbroadcastframes_g +=
			fxgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXMULTICASTFRAMES_G_POS,
				MMC_RISR_RXMULTICASTFRAMES_G_LEN))
		stats->rxmulticastframes_g +=
			fxgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXCRCERROR_POS,
				MMC_RISR_RXCRCERROR_LEN))
		stats->rxcrcerror += fxgmac_mmc_read(pdata, MMC_RXCRCERROR_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXALIGNERROR_POS,
				MMC_RISR_RXALIGNERROR_LEN))
		stats->rxalignerror += fxgmac_mmc_read(pdata, MMC_RXALIGNERROR);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXRUNTERROR_POS,
				MMC_RISR_RXRUNTERROR_LEN))
		stats->rxrunterror += fxgmac_mmc_read(pdata, MMC_RXRUNTERROR);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXJABBERERROR_POS,
				MMC_RISR_RXJABBERERROR_LEN))
		stats->rxjabbererror +=
			fxgmac_mmc_read(pdata, MMC_RXJABBERERROR);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXUNDERSIZE_G_POS,
				MMC_RISR_RXUNDERSIZE_G_LEN))
		stats->rxundersize_g +=
			fxgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOVERSIZE_G_POS,
				MMC_RISR_RXOVERSIZE_G_LEN))
		stats->rxoversize_g += fxgmac_mmc_read(pdata, MMC_RXOVERSIZE_G);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX64OCTETS_GB_POS,
				MMC_RISR_RX64OCTETS_GB_LEN))
		stats->rx64octets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX65TO127OCTETS_GB_POS,
				MMC_RISR_RX65TO127OCTETS_GB_LEN))
		stats->rx65to127octets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX128TO255OCTETS_GB_POS,
				MMC_RISR_RX128TO255OCTETS_GB_LEN))
		stats->rx128to255octets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX256TO511OCTETS_GB_POS,
				MMC_RISR_RX256TO511OCTETS_GB_LEN))
		stats->rx256to511octets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX512TO1023OCTETS_GB_POS,
				MMC_RISR_RX512TO1023OCTETS_GB_LEN))
		stats->rx512to1023octets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX1024TOMAXOCTETS_GB_POS,
				MMC_RISR_RX1024TOMAXOCTETS_GB_LEN))
		stats->rx1024tomaxoctets_gb +=
			fxgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXUNICASTFRAMES_G_POS,
				MMC_RISR_RXUNICASTFRAMES_G_LEN))
		stats->rxunicastframes_g +=
			fxgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXLENGTHERROR_POS,
				MMC_RISR_RXLENGTHERROR_LEN))
		stats->rxlengtherror +=
			fxgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOUTOFRANGETYPE_POS,
				MMC_RISR_RXOUTOFRANGETYPE_LEN))
		stats->rxoutofrangetype +=
			fxgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXPAUSEFRAMES_POS,
				MMC_RISR_RXPAUSEFRAMES_LEN))
		stats->rxpauseframes +=
			fxgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXFIFOOVERFLOW_POS,
				MMC_RISR_RXFIFOOVERFLOW_LEN))
		stats->rxfifooverflow +=
			fxgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXVLANFRAMES_GB_POS,
				MMC_RISR_RXVLANFRAMES_GB_LEN))
		stats->rxvlanframes_gb +=
			fxgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXWATCHDOGERROR_POS,
				MMC_RISR_RXWATCHDOGERROR_LEN))
		stats->rxwatchdogerror +=
			fxgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXERRORFRAMES_POS,
				MMC_RISR_RXERRORFRAMES_LEN))
		stats->rxreceiveerrorframe +=
			fxgmac_mmc_read(pdata, MMC_RXRECEIVEERRORFRAME);

	if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXERRORCONTROLFRAMES_POS,
				MMC_RISR_RXERRORCONTROLFRAMES_LEN))
		stats->rxcontrolframe_g +=
			fxgmac_mmc_read(pdata, MMC_RXCONTROLFRAME_G);
}

static void fxgmac_read_mmc_stats(struct fxgmac_pdata *pdata)
{
	struct fxgmac_stats *stats = &pdata->stats;
	u32 regval;

	/* Freeze counters */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, MMC_CR_MCF_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR);

	stats->txoctetcount_gb +=
		fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO);

	stats->txframecount_gb +=
		fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO);

	stats->txbroadcastframes_g +=
		fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO);

	stats->txmulticastframes_g +=
		fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO);

	stats->tx64octets_gb += fxgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO);

	stats->tx65to127octets_gb +=
		fxgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO);

	stats->tx128to255octets_gb +=
		fxgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO);

	stats->tx256to511octets_gb +=
		fxgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO);

	stats->tx512to1023octets_gb +=
		fxgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO);

	stats->tx1024tomaxoctets_gb +=
		fxgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO);

	stats->txunicastframes_gb +=
		fxgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO);

	stats->txmulticastframes_gb +=
		fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO);

	stats->txbroadcastframes_g +=
		fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO);

	stats->txunderflowerror +=
		fxgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO);

	stats->txsinglecollision_g +=
		fxgmac_mmc_read(pdata, MMC_TXSINGLECOLLISION_G);

	stats->txmultiplecollision_g +=
		fxgmac_mmc_read(pdata, MMC_TXMULTIPLECOLLISION_G);

	stats->txdeferredframes += fxgmac_mmc_read(pdata, MMC_TXDEFERREDFRAMES);

	stats->txlatecollisionframes +=
		fxgmac_mmc_read(pdata, MMC_TXLATECOLLISIONFRAMES);

	stats->txexcessivecollisionframes +=
		fxgmac_mmc_read(pdata, MMC_TXEXCESSIVECOLLSIONFRAMES);

	stats->txcarriererrorframes +=
		fxgmac_mmc_read(pdata, MMC_TXCARRIERERRORFRAMES);

	stats->txoctetcount_g += fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO);

	stats->txframecount_g += fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO);

	stats->txexcessivedeferralerror +=
		fxgmac_mmc_read(pdata, MMC_TXEXCESSIVEDEFERRALERROR);

	stats->txpauseframes += fxgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO);

	stats->txvlanframes_g += fxgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO);

	stats->txoversize_g += fxgmac_mmc_read(pdata, MMC_TXOVERSIZEFRAMES);

	stats->rxframecount_gb +=
		fxgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO);

	stats->rxoctetcount_gb +=
		fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO);

	stats->rxoctetcount_g += fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO);

	stats->rxbroadcastframes_g +=
		fxgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO);

	stats->rxmulticastframes_g +=
		fxgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO);

	stats->rxcrcerror += fxgmac_mmc_read(pdata, MMC_RXCRCERROR_LO);

	stats->rxalignerror += fxgmac_mmc_read(pdata, MMC_RXALIGNERROR);

	stats->rxrunterror += fxgmac_mmc_read(pdata, MMC_RXRUNTERROR);

	stats->rxjabbererror += fxgmac_mmc_read(pdata, MMC_RXJABBERERROR);

	stats->rxundersize_g += fxgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G);

	stats->rxoversize_g += fxgmac_mmc_read(pdata, MMC_RXOVERSIZE_G);

	stats->rx64octets_gb += fxgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO);

	stats->rx65to127octets_gb +=
		fxgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO);

	stats->rx128to255octets_gb +=
		fxgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO);

	stats->rx256to511octets_gb +=
		fxgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO);

	stats->rx512to1023octets_gb +=
		fxgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO);

	stats->rx1024tomaxoctets_gb +=
		fxgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO);

	stats->rxunicastframes_g +=
		fxgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO);

	stats->rxlengtherror += fxgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO);

	stats->rxoutofrangetype +=
		fxgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO);

	stats->rxpauseframes += fxgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO);

	stats->rxfifooverflow += fxgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO);

	stats->rxvlanframes_gb +=
		fxgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO);

	stats->rxwatchdogerror += fxgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR);

	stats->rxreceiveerrorframe +=
		fxgmac_mmc_read(pdata, MMC_RXRECEIVEERRORFRAME);

	stats->rxcontrolframe_g += fxgmac_mmc_read(pdata, MMC_RXCONTROLFRAME_G);

	/* Un-freeze counters */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, MMC_CR_MCF_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR);
}

static void fxgmac_config_mmc(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR);
	/* Set counters to reset on read */
	regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_ROR_POS, MMC_CR_ROR_LEN, 1);
	/* Reset the counters */
	regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_CR_POS, MMC_CR_CR_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR);

#if defined(FUXI_MISC_INT_HANDLE_FEATURE_EN) && FUXI_MISC_INT_HANDLE_FEATURE_EN
	writereg(pdata->pAdapter, 0xffffffff,
		 pdata->mac_regs + MMC_IPCRXINTMASK);
#endif
}

static int fxgmac_write_rss_reg(struct fxgmac_pdata *pdata, unsigned int type,
				unsigned int index, unsigned int val)
{
	int ret = 0;
	type = type;

	writereg(pdata->pAdapter, val, (pdata->base_mem + index));

	return ret;
}

static u32 fxgmac_read_rss_options(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL);

	/* Get the RSS options bits */
	regval = FXGMAC_GET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS,
				     MGMT_RSS_CTRL_OPT_LEN);

	return regval;
}

static int fxgmac_write_rss_options(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL);

	/* Set the RSS options */
	regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS,
				     MGMT_RSS_CTRL_OPT_LEN, pdata->rss_options);

	writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL));

	return 0;
}

#if !defined(DPDK)
static int fxgmac_read_rss_hash_key(struct fxgmac_pdata *pdata, u8 *key_buf)
{
	unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32);
	u32 *key = (u32 *)key_buf;

	while (key_regs--) {
		(*key) = cpu_to_be32(readreg(
			pdata->pAdapter,
			pdata->base_mem + (MGMT_RSS_KEY0 +
					   key_regs * MGMT_RSS_KEY_REG_INC)));

		DBGPRINT(
			MP_LOUD,
			("fxgmac_read_rss_hash_key: idx=%d, reg=%x, key=0x%08x\n",
			 key_regs,
			 MGMT_RSS_KEY0 + key_regs * MGMT_RSS_KEY_REG_INC,
			 (u32)(*key)));
		key++;
	}

	return 0;
}
#endif

static int fxgmac_write_rss_hash_key(struct fxgmac_pdata *pdata)
{
	unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32);
	u32 *key = (u32 *)&pdata->rss_key;
	int ret;

	while (key_regs--) {
		ret = fxgmac_write_rss_reg(
			pdata, FXGMAC_RSS_HASH_KEY_TYPE,
			MGMT_RSS_KEY0 + key_regs * MGMT_RSS_KEY_REG_INC,
			cpu_to_be32(*key));
		if (ret)
			return ret;
		key++;
	}

	return 0;
}

static int fxgmac_write_rss_lookup_table(struct fxgmac_pdata *pdata)
{
	unsigned int i, j;
	u32 regval = 0;
	int ret;

	for (i = 0, j = 0; i < ARRAY_SIZE(pdata->rss_table); i++, j++) {
		if (j < MGMT_RSS_IDT_ENTRY_PER_REG) {
			regval |=
				((pdata->rss_table[i] & MGMT_RSS_IDT_ENTRY_MASK)
				 << (j * 2));
		} else {
			ret = fxgmac_write_rss_reg(
				pdata, FXGMAC_RSS_LOOKUP_TABLE_TYPE,
				MGMT_RSS_IDT + (i / MGMT_RSS_IDT_ENTRY_PER_REG -
						1) * MGMT_RSS_IDT_REG_INC,
				regval);
			if (ret)
				return ret;

			regval = pdata->rss_table[i];
			j = 0;
		}
	}

	if (j == MGMT_RSS_IDT_ENTRY_PER_REG) {
		/* last IDT */
		fxgmac_write_rss_reg(
			pdata, FXGMAC_RSS_LOOKUP_TABLE_TYPE,
			MGMT_RSS_IDT + (i / MGMT_RSS_IDT_ENTRY_PER_REG - 1) *
					       MGMT_RSS_IDT_REG_INC,
			regval);
	}

	return 0;
}

static int fxgmac_set_rss_hash_key(struct fxgmac_pdata *pdata, const u8 *key)
{
	memcpy(pdata->rss_key, key, sizeof(pdata->rss_key));

	return fxgmac_write_rss_hash_key(pdata);
}

static int fxgmac_set_rss_lookup_table(struct fxgmac_pdata *pdata,
				       const u32 *table)
{
	unsigned int i;
	u32 tval;

#if FXGMAC_MSIX_CH0RXDIS_EN
	DPRINTK("Set_rss_table, rss ctrl eth=0x%08x\n", 0);

	return 0;
#endif

	for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) {
		tval = table[i];
		pdata->rss_table[i] = FXGMAC_SET_REG_BITS(pdata->rss_table[i],
							  MAC_RSSDR_DMCH_POS,
							  MAC_RSSDR_DMCH_LEN,
							  tval);
	}

	return fxgmac_write_rss_lookup_table(pdata);
}

static u32 log2ex(u32 value)
{
	u32 i = 31;
	while (i > 0) {
		if (value & 0x80000000) {
			break;
		}
		value <<= 1;
		i--;
	}
	return i;
}

static int fxgmac_enable_rss(struct fxgmac_pdata *pdata)
{
	u32 regval;
	u32 size = 0;

	int ret;

	if (!pdata->hw_feat.rss) {
		return -EOPNOTSUPP;
	}

	/* Program the hash key */
	ret = fxgmac_write_rss_hash_key(pdata);
	if (ret) {
		return ret;
	}

	/* Program the lookup table */
	ret = fxgmac_write_rss_lookup_table(pdata);
	if (ret) {
		return ret;
	}

	regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL);

	/* Set RSS IDT table size */
	size = log2ex(FXGMAC_RSS_MAX_TABLE_SIZE) - 1;
	regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_TBL_SIZE_POS,
				     MGMT_RSS_CTRL_TBL_SIZE_LEN, size);

#if FXGMAC_MSIX_CH0RXDIS_EN
	/* set default cpu id to 1 */
	regval = FXGMAC_SET_REG_BITS(regval, 8, 2, 1);
#endif
	/* Enable RSS */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS,
				     MAC_RSSCR_RSSE_LEN, 1);

	/* Set the RSS options */
	regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS,
				     MGMT_RSS_CTRL_OPT_LEN, pdata->rss_options);

	writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL));
	DPRINTK("enable_rss callout, rss ctrl reg=0x%08x\n", regval);

	return 0;
}

static int fxgmac_disable_rss(struct fxgmac_pdata *pdata)
{
	u32 regval;

	if (!pdata->hw_feat.rss)
		return -EOPNOTSUPP;

#if FXGMAC_MSIX_CH0RXDIS_EN
	DPRINTK("Disable_rss, rss ctrl eth=0x%08x\n", 0);

	return 0;
#endif

	regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS,
				     MAC_RSSCR_RSSE_LEN, 0);

	writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL));
	DPRINTK("disable_rss, rss ctrl reg=0x%08x\n", regval);

	return 0;
}

static void fxgmac_config_rss(struct fxgmac_pdata *pdata)
{
	int ret;

	if (!pdata->hw_feat.rss)
		return;

	if (pdata->rss)
		ret = fxgmac_enable_rss(pdata);
	else
		ret = fxgmac_disable_rss(pdata);

	if (ret) {
		DBGPRINT(MP_ERROR,
			 ("fxgmac_config_rss: error configuring RSS\n"));
	}
}

static void fxgmac_update_aoe_ipv4addr(struct fxgmac_pdata *pdata, u8 *ip_addr)
{
	unsigned int regval, ipval = 0;

	/* enable or disable ARP offload engine. */
	if (!pdata->hw_feat.aoe) {
		netdev_err(
			pdata->netdev,
			"error update ip addr - arp offload not supported.\n");
		return;
	}

	if (ip_addr) {
		ipval = (ip_addr[0] << 24) | (ip_addr[1] << 16) |
			(ip_addr[2] << 8) | (ip_addr[3] << 0);
		DPRINTK("%s, covert IP dotted-addr %s to binary 0x%08x ok.\n",
			__FUNCTION__, ip_addr, cpu_to_be32(ipval));
	} else {
		/* get ipv4 addr from net device */
		ipval = fxgmac_get_netdev_ip4addr(pdata);
		DPRINTK("%s, Get net device binary IP ok, 0x%08x\n",
			__FUNCTION__, cpu_to_be32(ipval));

		ipval = cpu_to_be32(ipval);
	}

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ARP_PROTO_ADDR);
	if (regval != /*cpu_to_be32*/ (ipval)) {
		writereg(pdata->pAdapter, /*cpu_to_be32*/ (ipval),
			 pdata->mac_regs + MAC_ARP_PROTO_ADDR);
		DPRINTK("%s, update arp ipaddr reg from 0x%08x to 0x%08x\n",
			__FUNCTION__, regval, /*cpu_to_be32*/ (ipval));
	}
}

static int fxgmac_enable_arp_offload(struct fxgmac_pdata *pdata)
{
	u32 regval;

	if (!pdata->hw_feat.aoe)
		return -EOPNOTSUPP;

	/* Enable arpoffload */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN,
				     1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

static int fxgmac_disable_arp_offload(struct fxgmac_pdata *pdata)
{
	u32 regval;

	if (!pdata->hw_feat.aoe)
		return -EOPNOTSUPP;
	/* disable arpoffload */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN,
				     0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

/* this function config register for NS offload function
 * parameters:
 * 	index - 0~1, index to NS look up table. one entry of the lut is like this |remote|solicited|target0|target1|
 *	remote_addr - ipv6 addr where fuxi gets the NS solicitation pkt(request). in common, it is 0 to match any remote machine.
 *	solicited_addr - the solicited node multicast group address which fuxi computes and joins.
 *	target_addr1 - it is the target address in NS solicitation pkt.
 *	target_addr2 - second target address, any address (with last 6B same with target address?).
 */
static int fxgmac_set_ns_offload(struct fxgmac_pdata *pdata, unsigned int index,
				 unsigned char *remote_addr,
				 unsigned char *solicited_addr,
				 unsigned char *target_addr1,
				 unsigned char *target_addr2,
				 unsigned char *mac_addr)
{
	u32 regval;
	u32 Address[4], mac_addr_hi, mac_addr_lo;
	u8 i, remote_not_zero = 0;

	regval = readreg(pdata->pAdapter, pdata->base_mem + NS_TPID_PRO);
	regval = FXGMAC_SET_REG_BITS(regval, NS_TPID_PRO_STPID_POS,
				     NS_TPID_PRO_STPID_LEN, 0X8100);
	regval = FXGMAC_SET_REG_BITS(regval, NS_TPID_PRO_CTPID_POS,
				     NS_TPID_PRO_CTPID_LEN, 0X9100);
	writereg(pdata->pAdapter, regval, pdata->base_mem + NS_TPID_PRO);
	regval = readreg(pdata->pAdapter,
			 pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_DST_CMP_TYPE_POS,
				     NS_LUT_DST_CMP_TYPE_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_DST_IGNORED_POS,
				     NS_LUT_DST_IGNORED_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS,
				     NS_LUT_REMOTE_AWARED_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_TARGET_ISANY_POS,
				     NS_LUT_TARGET_ISANY_LEN, 0);
	writereg(pdata->pAdapter, regval,
		 pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL);

	/* AR */
	for (i = 0; i < 16 / 4; i++) {
		Address[i] = (remote_addr[i * 4 + 0] << 24) |
			     (remote_addr[i * 4 + 1] << 16) |
			     (remote_addr[i * 4 + 2] << 8) |
			     (remote_addr[i * 4 + 3] << 0);
		writereg(pdata->pAdapter, Address[i],
			 pdata->base_mem + 0X38 * index + NS_LUT_ROMOTE0 +
				 4 * i);
		if (Address[i]) {
			remote_not_zero = 1;
		}
		Address[i] = (target_addr1[i * 4 + 0] << 24) |
			     (target_addr1[i * 4 + 1] << 16) |
			     (target_addr1[i * 4 + 2] << 8) |
			     (target_addr1[i * 4 + 3] << 0);
		writereg(pdata->pAdapter, Address[i],
			 pdata->base_mem + 0X38 * index + NS_LUT_TARGET0 +
				 4 * i);
		Address[i] = (solicited_addr[i * 4 + 0] << 24) |
			     (solicited_addr[i * 4 + 1] << 16) |
			     (solicited_addr[i * 4 + 2] << 8) |
			     (solicited_addr[i * 4 + 3] << 0);
		writereg(pdata->pAdapter, Address[i],
			 pdata->base_mem + 0X38 * index + NS_LUT_SOLICITED0 +
				 4 * i);
		Address[i] = (target_addr2[i * 4 + 0] << 24) |
			     (target_addr2[i * 4 + 1] << 16) |
			     (target_addr2[i * 4 + 2] << 8) |
			     (target_addr2[i * 4 + 3] << 0);
		writereg(pdata->pAdapter, Address[i],
			 pdata->base_mem + 0X10 * index + NS_LUT_TARGET4 +
				 4 * i);
	}
	mac_addr_hi = (mac_addr[0] << 24) | (mac_addr[1] << 16) |
		      (mac_addr[2] << 8) | (mac_addr[3] << 0);
	mac_addr_lo = (mac_addr[4] << 8) | (mac_addr[5] << 0);

	writereg(pdata->pAdapter, mac_addr_hi,
		 pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR);
	if (remote_not_zero == 0) {
		regval = readreg(pdata->pAdapter, pdata->base_mem +
							  0X38 * index +
							  NS_LUT_MAC_ADDR_CTL);
		regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS,
					     NS_LUT_REMOTE_AWARED_LEN, 0);
		regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_MAC_ADDR_LOW_POS,
					     NS_LUT_MAC_ADDR_LOW_LEN,
					     mac_addr_lo);
		writereg(pdata->pAdapter, regval,
			 pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL);
	} else {
		regval = readreg(pdata->pAdapter, pdata->base_mem +
							  0X38 * index +
							  NS_LUT_MAC_ADDR_CTL);
		regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS,
					     NS_LUT_REMOTE_AWARED_LEN, 1);
		regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_MAC_ADDR_LOW_POS,
					     NS_LUT_MAC_ADDR_LOW_LEN,
					     mac_addr_lo);
		writereg(pdata->pAdapter, regval,
			 pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL);
	}
	return 0;
}

static void fxgmac_update_ns_offload_ipv6addr(struct fxgmac_pdata *pdata,
					      unsigned int param)
{
	struct net_device *netdev = pdata->netdev;
	unsigned char addr_buf[5][16];

	unsigned char *remote_addr = (unsigned char *)&addr_buf[0][0];
	unsigned char *solicited_addr = (unsigned char *)&addr_buf[1][0];
	unsigned char *target_addr1 = (unsigned char *)&addr_buf[2][0];
	unsigned char *mac_addr = (unsigned char *)&addr_buf[4][0];

	/* get ipv6 addr from net device */
	if (NULL == fxgmac_get_netdev_ip6addr(pdata, target_addr1,
					      solicited_addr,
					      (FXGMAC_NS_IFA_LOCAL_LINK |
					       FXGMAC_NS_IFA_GLOBAL_UNICAST) &
						      param)) {
		DPRINTK("%s, get net device ipv6 addr with err and ignore NS offload.\n",
			__FUNCTION__);

		return;
	}

	DPRINTK("%s, Get net device binary IPv6 ok, local-link=%pI6\n",
		__FUNCTION__, target_addr1);
	DPRINTK("%s, Get net device binary IPv6 ok, solicited =%pI6\n",
		__FUNCTION__, solicited_addr);

	memcpy(mac_addr, netdev->dev_addr, netdev->addr_len);
	DPRINTK("%s, Get net device MAC addr ok, ns_tab idx=%d, %02x:%02x:%02x:%02x:%02x:%02x\n",
		__FUNCTION__, pdata->expansion.ns_offload_tab_idx, mac_addr[0],
		mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4],
		mac_addr[5]);

	memset(remote_addr, 0, 16);
	fxgmac_set_ns_offload(pdata, pdata->expansion.ns_offload_tab_idx++,
			      remote_addr, solicited_addr, target_addr1,
			      target_addr1, mac_addr);
	if (pdata->expansion.ns_offload_tab_idx >= 2)
		pdata->expansion.ns_offload_tab_idx = 0;
}

static int fxgmac_enable_ns_offload(struct fxgmac_pdata *pdata)
{
	writereg(pdata->pAdapter, 0X00000011, pdata->base_mem + NS_OF_GLB_CTL);
	return 0;
}

static int fxgmac_disable_ns_offload(struct fxgmac_pdata *pdata)
{
	writereg(pdata->pAdapter, 0X00000000, pdata->base_mem + NS_OF_GLB_CTL);
	return 0;
}

static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata)
{
	u32 regval;
	int ret = 0;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS,
				     MAC_PMT_STA_RWKFILTERST_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_GET_REG_BITS(regval, MAC_PMT_STA_RWKPTR_POS,
				     MAC_PMT_STA_RWKPTR_LEN);
	if (regval != 0) {
		DPRINTK("Remote fifo pointer  is not 0\n");
		ret = -EINVAL;
	}
	return ret;
}

static int fxgmac_set_wake_pattern_mask(struct fxgmac_pdata *pdata,
					u32 filter_index, u8 register_index,
					u32 Data)
{
	const u16 address_offset[16][3] = {
		{ 0x1020, 0x1024, 0x1028 }, { 0x102c, 0x1030, 0x1034 },
		{ 0x1038, 0x103c, 0x1040 }, { 0x1044, 0x1050, 0x1054 },
		{ 0x1058, 0x105c, 0x1060 }, { 0x1064, 0x1068, 0x106c },
		{ 0x1070, 0x1074, 0x1078 }, { 0x107c, 0x1080, 0x1084 },
		{ 0x1088, 0x108c, 0x1090 }, { 0x1134, 0x113c, 0x1140 },
		{ 0x1208, 0x1200, 0x1204 }, { 0x1218, 0x1210, 0x1214 },
		{ 0x1228, 0x1220, 0x1224 }, { 0x1238, 0x1230, 0x1234 },
		{ 0x1248, 0x1240, 0x1244 }, { 0x1258, 0x1250, 0x1254 },
	};
	if (filter_index > 15 || register_index > 2) {
		DbgPrintF(
			MP_TRACE,
			"%s - Remote mask  pointer  is  over range, filter_index:%d, register_index:0x%x\n",
			__FUNCTION__, filter_index, register_index);
		return -1;
	}
	writereg(pdata->pAdapter, Data,
		 pdata->base_mem +
			 address_offset[filter_index][register_index]);
	return 0;
}

static u16 wol_crc16(u8 *pucframe, u16 uslen)
{
	int i;

	union type16 {
		u16 raw;
		struct {
			u16 bit_0 : 1;
			u16 bit_1 : 1;
			u16 bit_2 : 1;
			u16 bit_3 : 1;
			u16 bit_4 : 1;
			u16 bit_5 : 1;
			u16 bit_6 : 1;
			u16 bit_7 : 1;
			u16 bit_8 : 1;
			u16 bit_9 : 1;
			u16 bit_10 : 1;
			u16 bit_11 : 1;
			u16 bit_12 : 1;
			u16 bit_13 : 1;
			u16 bit_14 : 1;
			u16 bit_15 : 1;
		} bits;
	};

	union type8 {
		u16 raw;

		struct {
			u16 bit_0 : 1;
			u16 bit_1 : 1;
			u16 bit_2 : 1;
			u16 bit_3 : 1;
			u16 bit_4 : 1;
			u16 bit_5 : 1;
			u16 bit_6 : 1;
			u16 bit_7 : 1;
		} bits;
	};

	union type16 crc, crc_comb;
	union type8 next_crc, rrpe_data;
	next_crc.raw = 0;
	crc.raw = 0xffff;
	for (i = 0; i < uslen; i++) {
		rrpe_data.raw = pucframe[i];
		next_crc.bits.bit_0 = crc.bits.bit_15 ^ rrpe_data.bits.bit_0;
		next_crc.bits.bit_1 = crc.bits.bit_14 ^ next_crc.bits.bit_0 ^
				      rrpe_data.bits.bit_1;
		next_crc.bits.bit_2 = crc.bits.bit_13 ^ next_crc.bits.bit_1 ^
				      rrpe_data.bits.bit_2;
		next_crc.bits.bit_3 = crc.bits.bit_12 ^ next_crc.bits.bit_2 ^
				      rrpe_data.bits.bit_3;
		next_crc.bits.bit_4 = crc.bits.bit_11 ^ next_crc.bits.bit_3 ^
				      rrpe_data.bits.bit_4;
		next_crc.bits.bit_5 = crc.bits.bit_10 ^ next_crc.bits.bit_4 ^
				      rrpe_data.bits.bit_5;
		next_crc.bits.bit_6 = crc.bits.bit_9 ^ next_crc.bits.bit_5 ^
				      rrpe_data.bits.bit_6;
		next_crc.bits.bit_7 = crc.bits.bit_8 ^ next_crc.bits.bit_6 ^
				      rrpe_data.bits.bit_7;

		crc_comb.bits.bit_15 = crc.bits.bit_7 ^ next_crc.bits.bit_7;
		crc_comb.bits.bit_14 = crc.bits.bit_6;
		crc_comb.bits.bit_13 = crc.bits.bit_5;
		crc_comb.bits.bit_12 = crc.bits.bit_4;
		crc_comb.bits.bit_11 = crc.bits.bit_3;
		crc_comb.bits.bit_10 = crc.bits.bit_2;
		crc_comb.bits.bit_9 = crc.bits.bit_1 ^ next_crc.bits.bit_0;
		crc_comb.bits.bit_8 = crc.bits.bit_0 ^ next_crc.bits.bit_1;
		crc_comb.bits.bit_7 = next_crc.bits.bit_0 ^ next_crc.bits.bit_2;
		crc_comb.bits.bit_6 = next_crc.bits.bit_1 ^ next_crc.bits.bit_3;
		crc_comb.bits.bit_5 = next_crc.bits.bit_2 ^ next_crc.bits.bit_4;
		crc_comb.bits.bit_4 = next_crc.bits.bit_3 ^ next_crc.bits.bit_5;
		crc_comb.bits.bit_3 = next_crc.bits.bit_4 ^ next_crc.bits.bit_6;
		crc_comb.bits.bit_2 = next_crc.bits.bit_5 ^ next_crc.bits.bit_7;
		crc_comb.bits.bit_1 = next_crc.bits.bit_6;
		crc_comb.bits.bit_0 = next_crc.bits.bit_7;
		crc.raw = crc_comb.raw;
	}
	return crc.raw;
}

static int fxgmac_set_wake_pattern(struct fxgmac_pdata *pdata,
				   struct wol_bitmap_pattern *wol_pattern,
				   u32 pattern_cnt)
{
	u32 i, j, kp, km, mask_index;
	int z;
	u16 map_index;
	u8 mask[MAX_PATTERN_SIZE];
	u32 regval = 0;
	u32 total_cnt = 0, pattern_inherited_cnt = 0;
	u8 *ptdata, *ptmask;

	if (pattern_cnt > MAX_PATTERN_COUNT) {
		DbgPrintF(
			MP_TRACE,
			"%s - Error: %d patterns, exceed %d, not supported!\n",
			__FUNCTION__, pattern_cnt, MAX_PATTERN_COUNT);
		return -1;
	}

	/* Reset the FIFO head pointer. */
	if (fxgmac_check_wake_pattern_fifo_pointer(pdata)) {
		DbgPrintF(
			MP_TRACE,
			"%s - Warning: the remote pattern array pointer is not be 0\n",
			__FUNCTION__);
		return -1;
	}

	for (i = 0; i < pattern_cnt; i++) {
		memcpy(&pdata->pattern[i], wol_pattern + i,
		       sizeof(wol_pattern[0]));
		if (pattern_cnt + pattern_inherited_cnt < MAX_PATTERN_COUNT) {
			if (wol_pattern[i].pattern_offset ||
			    !(wol_pattern[i].mask_info[0] & 0x01)) {
				memcpy(&pdata->pattern[pattern_cnt +
						       pattern_inherited_cnt],
				       wol_pattern + i, sizeof(wol_pattern[0]));
				pattern_inherited_cnt++;
			}
		}
	}
	total_cnt = pattern_cnt + pattern_inherited_cnt;

	/*
	 * calculate  the crc-16 of the mask pattern
	 * print the pattern and mask for debug purpose.
	 */
	for (i = 0; i < total_cnt; i++) {
		/* Please program pattern[i] to NIC for pattern match wakeup.
		 * pattern_size, pattern_info, mask_info
		 */
		/* save the mask pattern */
		mask_index = 0;
		map_index = 0;
		for (j = 0; j < pdata->pattern[i].mask_size; j++) {
			for (z = 0;
			     z < ((j == (MAX_PATTERN_SIZE / 8 - 1)) ? 7 : 8);
			     z++) {
				if (pdata->pattern[i].mask_info[j] &
				    (0x01 << z)) {
					mask[map_index] =
						pdata->pattern[i].pattern_info
							[pdata->pattern[i]
								 .pattern_offset +
							 mask_index];
					map_index++;
				}
				mask_index++;
			}
		}
		/* calculate  the crc-16 of the mask pattern */
		pdata->pattern[i].pattern_crc = wol_crc16(mask, map_index);

		/* Print pattern match, for debug purpose. */
		DbgPrintF(MP_LOUD, "%s - Pattern[%d]:", __FUNCTION__, i);
		for (kp = 0, km = 0;
		     kp < sizeof(pdata->pattern[i].pattern_info);
		     kp += 16, km += 2) {
			ptdata = &pdata->pattern[i].pattern_info[kp];
			ptmask = &pdata->pattern[i].mask_info[km];
			DBGPRINT(
				MP_LOUD,
				("\n    %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x  Mask %02x-%02x",
				 ptdata[0], ptdata[1], ptdata[2], ptdata[3],
				 ptdata[4], ptdata[5], ptdata[6], ptdata[7],
				 ptdata[8], ptdata[9], ptdata[10], ptdata[11],
				 ptdata[12], ptdata[13], ptdata[14], ptdata[15],
				 ptmask[0], ptmask[1]));
		}
		DbgPrintF(
			MP_LOUD,
			"WritePatternToNic62 the %d patterns crc = %x mask length = %d, mask_offset=%x.\n",
			i, pdata->pattern[i].pattern_crc, map_index,
			pdata->pattern[i].pattern_offset);
		memset(mask, 0, sizeof(mask));
	}

	/* Write patterns by FIFO block. */
	for (i = 0; i < (total_cnt + 3) / 4; i++) {
		/* 1. Write the first 4Bytes of Filter. */
		writereg(pdata->pAdapter,
			 ((pdata->pattern[i * 4 + 0].mask_info[3] & 0x7f)
			  << 24) |
				 (pdata->pattern[i * 4 + 0].mask_info[2]
				  << 16) |
				 (pdata->pattern[i * 4 + 0].mask_info[1] << 8) |
				 (pdata->pattern[i * 4 + 0].mask_info[0] << 0),
			 pdata->mac_regs + MAC_RWK_PAC);

		writereg(pdata->pAdapter,
			 ((pdata->pattern[i * 4 + 1].mask_info[3] & 0x7f)
			  << 24) |
				 (pdata->pattern[i * 4 + 1].mask_info[2]
				  << 16) |
				 (pdata->pattern[i * 4 + 1].mask_info[1] << 8) |
				 (pdata->pattern[i * 4 + 1].mask_info[0] << 0),
			 pdata->mac_regs + MAC_RWK_PAC);

		writereg(pdata->pAdapter,
			 ((pdata->pattern[i * 4 + 2].mask_info[3] & 0x7f)
			  << 24) |
				 (pdata->pattern[i * 4 + 2].mask_info[2]
				  << 16) |
				 (pdata->pattern[i * 4 + 2].mask_info[1] << 8) |
				 (pdata->pattern[i * 4 + 2].mask_info[0] << 0),
			 pdata->mac_regs + MAC_RWK_PAC);

		writereg(pdata->pAdapter,
			 ((pdata->pattern[i * 4 + 3].mask_info[3] & 0x7f)
			  << 24) |
				 (pdata->pattern[i * 4 + 3].mask_info[2]
				  << 16) |
				 (pdata->pattern[i * 4 + 3].mask_info[1] << 8) |
				 (pdata->pattern[i * 4 + 3].mask_info[0] << 0),
			 pdata->mac_regs + MAC_RWK_PAC);

		/* 2. Write the Filter Command. */
		regval = 0;
		/* Set filter enable bit. */
		regval |= ((i * 4 + 0) < total_cnt) ? (0x1 << 0) : 0x0;
		regval |= ((i * 4 + 1) < total_cnt) ? (0x1 << 8) : 0x0;
		regval |= ((i * 4 + 2) < total_cnt) ? (0x1 << 16) : 0x0;
		regval |= ((i * 4 + 3) < total_cnt) ? (0x1 << 24) : 0x0;
		/* Set filter address type, 0- unicast, 1 - multicast. */
		regval |= (i * 4 + 0 >= total_cnt)   ? 0x0 :
			  (i * 4 + 0 >= pattern_cnt) ? (0x1 << (3 + 0)) :
			  pdata->pattern[i * 4 + 0].pattern_offset ?
						       0x0 :
			  !(pdata->pattern[i * 4 + 0].mask_info[0] & 0x01) ?
						       0x0 :
			  (pdata->pattern[i * 4 + 0].pattern_info[0] & 0x01) ?
						       (0x1 << (3 + 0)) :
						       0x0;
		regval |= (i * 4 + 1 >= total_cnt)   ? 0x0 :
			  (i * 4 + 1 >= pattern_cnt) ? (0x1 << (3 + 8)) :
			  pdata->pattern[i * 4 + 1].pattern_offset ?
						       0x0 :
			  !(pdata->pattern[i * 4 + 1].mask_info[0] & 0x01) ?
						       0x0 :
			  (pdata->pattern[i * 4 + 1].pattern_info[0] & 0x01) ?
						       (0x1 << (3 + 8)) :
						       0x0;
		regval |= (i * 4 + 2 >= total_cnt)   ? 0x0 :
			  (i * 4 + 2 >= pattern_cnt) ? (0x1 << (3 + 16)) :
			  pdata->pattern[i * 4 + 2].pattern_offset ?
						       0x0 :
			  !(pdata->pattern[i * 4 + 2].mask_info[0] & 0x01) ?
						       0x0 :
			  (pdata->pattern[i * 4 + 2].pattern_info[0] & 0x01) ?
						       (0x1 << (3 + 16)) :
						       0x0;
		regval |= (i * 4 + 3 >= total_cnt)   ? 0x0 :
			  (i * 4 + 3 >= pattern_cnt) ? (0x1 << (3 + 24)) :
			  pdata->pattern[i * 4 + 3].pattern_offset ?
						       0x0 :
			  !(pdata->pattern[i * 4 + 3].mask_info[0] & 0x01) ?
						       0x0 :
			  (pdata->pattern[i * 4 + 3].pattern_info[0] & 0x01) ?
						       (0x1 << (3 + 24)) :
						       0x0;
		writereg(pdata->pAdapter, regval,
			 pdata->mac_regs + MAC_RWK_PAC);

		/* 3. Write the mask offset. */
		writereg(pdata->pAdapter,
			 (pdata->pattern[i * 4 + 3].pattern_offset << 24) |
				 (pdata->pattern[i * 4 + 2].pattern_offset
				  << 16) |
				 (pdata->pattern[i * 4 + 1].pattern_offset
				  << 8) |
				 (pdata->pattern[i * 4 + 0].pattern_offset
				  << 0),
			 pdata->mac_regs + MAC_RWK_PAC);

		/* 4. Write the masked data CRC. */
		writereg(pdata->pAdapter,
			 (pdata->pattern[i * 4 + 1].pattern_crc << 16) |
				 (pdata->pattern[i * 4 + 0].pattern_crc << 0),
			 pdata->mac_regs + MAC_RWK_PAC);
		writereg(pdata->pAdapter,
			 (pdata->pattern[i * 4 + 3].pattern_crc << 16) |
				 (pdata->pattern[i * 4 + 2].pattern_crc << 0),
			 pdata->mac_regs + MAC_RWK_PAC);
	}

	for (i = 0; i < total_cnt; i++) {
		fxgmac_set_wake_pattern_mask(
			pdata, i, 0,
			((pdata->pattern[i].mask_info[7] & 0x7f) << (24 + 1)) |
				(pdata->pattern[i].mask_info[6] << (16 + 1)) |
				(pdata->pattern[i].mask_info[5] << (8 + 1)) |
				(pdata->pattern[i].mask_info[4] << (0 + 1)) |
				((pdata->pattern[i].mask_info[3] & 0x80) >>
				 7)); /* global  manager  regitster  mask bit  31~62 */
		fxgmac_set_wake_pattern_mask(
			pdata, i, 1,
			((pdata->pattern[i].mask_info[11] & 0x7f) << (24 + 1)) |
				(pdata->pattern[i].mask_info[10] << (16 + 1)) |
				(pdata->pattern[i].mask_info[9] << (8 + 1)) |
				(pdata->pattern[i].mask_info[8] << (0 + 1)) |
				((pdata->pattern[i].mask_info[7] & 0x80) >>
				 7)); /* global  manager  regitster  mask bit  63~94 */
		fxgmac_set_wake_pattern_mask(
			pdata, i, 2,
			((pdata->pattern[i].mask_info[15] & 0x7f) << (24 + 1)) |
				(pdata->pattern[i].mask_info[14] << (16 + 1)) |
				(pdata->pattern[i].mask_info[13] << (8 + 1)) |
				(pdata->pattern[i].mask_info[12] << (0 + 1)) |
				((pdata->pattern[i].mask_info[11] & 0x80) >>
				 7)); /* global  manager  regitster  mask bit  95~126 */
	}

	return 0;
}

static int fxgmac_enable_wake_pattern(struct fxgmac_pdata *pdata)
{
	u32 regval;
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS,
				     MAC_PMT_STA_RWKFILTERST_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKPKTEN_POS,
				     MAC_PMT_STA_RWKPKTEN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);
	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);
	return 0;
}

static int fxgmac_disable_wake_pattern(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS,
				     MAC_PMT_STA_RWKFILTERST_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKPKTEN_POS,
				     MAC_PMT_STA_RWKPKTEN_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);
	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);
	return 0;
}

static int fxgmac_enable_wake_magic_pattern(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_MGKPKTEN_POS,
				     MAC_PMT_STA_MGKPKTEN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);
	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);

	/* Enable PME Enable Bit. */
	cfg_r32(pdata, REG_PM_STATCTRL, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, PM_CTRLSTAT_PME_EN_POS,
				     PM_CTRLSTAT_PME_EN_LEN, 1);
	cfg_w32(pdata, REG_PM_STATCTRL, regval);

	return 0;
}

static int fxgmac_disable_wake_magic_pattern(struct fxgmac_pdata *pdata)
{
	u32 regval;
	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_MGKPKTEN_POS,
				     MAC_PMT_STA_MGKPKTEN_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);
	return 0;
}

#if defined(FUXI_PM_WPI_READ_FEATURE_EN) && FUXI_PM_WPI_READ_FEATURE_EN
/*
 * enable Wake packet indication. called to enable before sleep/hibernation
 * and no needed to call disable for that, fxgmac_get_wake_packet_indication will clear to normal once done.
 */
static void fxgmac_enable_wake_packet_indication(struct fxgmac_pdata *pdata,
						 int en)
{
	u32 val_wpi_crtl0;

	/* read-clear WoL event. */
	readreg(pdata->pAdapter, pdata->base_mem + MGMT_WOL_CTRL);

	/* get wake packet information */
	val_wpi_crtl0 =
		(u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0);

	/* prepare to write packet data by write wpi_mode to 1 */
	val_wpi_crtl0 =
		FXGMAC_SET_REG_BITS(val_wpi_crtl0, MGMT_WPI_CTRL0_WPI_MODE_POS,
				    MGMT_WPI_CTRL0_WPI_MODE_LEN,
				    (en ? MGMT_WPI_CTRL0_WPI_MODE_WR :
					  MGMT_WPI_CTRL0_WPI_MODE_NORMAL));
	writereg(pdata->pAdapter, val_wpi_crtl0,
		 pdata->base_mem + MGMT_WPI_CTRL0);

	DbgPrintF(MP_TRACE, "%s - WPI pkt enable=%d, reg=%08x.\n", __FUNCTION__,
		  en, val_wpi_crtl0);

	return;
}

/*
 * this function read Wake up packet after MDIS resume
 * input:
 * 		pdata
 *		wpi_buf		container of a packet.
 *		buf_size	size of the packet container. since HW limit to 14bits, ie 16KB all together.
 * output:
 * 		wake_reason	from HW, we can indentify 1)magic packet, or 2)pattern(remote wake packet) or WAKE_REASON_HW_ERR indicates err
 *		packet_size length of the wake packet. 0 indicates exception.
 *
 */
static void fxgmac_get_wake_packet_indication(struct fxgmac_pdata *pdata,
					      int *wake_reason,
					      u32 *wake_pattern_number,
					      u8 *wpi_buf, u32 buf_size,
					      u32 *packet_size)
{
	u32 i, regval, val_wpi_crtl0, *dw_wpi_buf;
	u32 data_len, data_len_dw, b_need_pkt = 0;

	*wake_reason = WAKE_REASON_NONE;
	*packet_size = 0;
	fxgmac_release_phy(pdata);

	/* try to check wake reason. GMAC reg 20c0 only tells Magic or remote-pattern
	 * read from MGMT_WOL_CTRL, 1530 instead.
	 */
	regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WOL_CTRL);
	DbgPrintF(MP_TRACE, "%s - 0x1530=%x.\n", __FUNCTION__, regval);
	if (!regval) {
		DbgPrintF(MP_TRACE, "%s - nothing for WPI pkt.\n",
			  __FUNCTION__);
		return;
	}

	if (regval & MGMT_WOL_CTRL_WPI_MGC_PKT) {
		*wake_reason = WAKE_REASON_MAGIC;
		b_need_pkt = 1;
	} else if (regval & MGMT_WOL_CTRL_WPI_RWK_PKT) {
		*wake_reason = WAKE_REASON_PATTERNMATCH;
		b_need_pkt = 1;
		*wake_pattern_number = 0;

		/*
		 * wake_pattern_number, HW should tell, tbd
		 */
		for (i = 0; i < MAX_PATTERN_COUNT; i++) {
			if (regval & (MGMT_WOL_CTRL_WPI_RWK_PKT_NUMBER << i)) {
				*wake_pattern_number = i;
				break;
			}
		}

	} else if (regval & MGMT_WOL_CTRL_WPI_LINK_CHG) {
		*wake_reason = WAKE_REASON_LINK;
	}

	if (!b_need_pkt) {
		DbgPrintF(MP_TRACE, "%s - wake by link and no WPI pkt.\n",
			  __FUNCTION__);
		return;
	}

	/* get wake packet information */
	val_wpi_crtl0 =
		(u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0);

	if (val_wpi_crtl0 & MGMT_WPI_CTRL0_WPI_FAIL) {
		*wake_reason = WAKE_REASON_HW_ERR;
		DbgPrintF(MP_TRACE, "%s - WPI pkt fail from hw.\n",
			  __FUNCTION__);
		return;
	}

	*packet_size = FXGMAC_GET_REG_BITS(val_wpi_crtl0,
					   MGMT_WPI_CTRL0_WPI_PKT_LEN_POS,
					   MGMT_WPI_CTRL0_WPI_PKT_LEN_LEN);

	if (0 == *packet_size) {
		*wake_reason = WAKE_REASON_HW_ERR;
		DbgPrintF(MP_TRACE, "%s - WPI pkt len is 0 from hw.\n",
			  __FUNCTION__);
		return;
	}

	DbgPrintF(MP_TRACE, "%s - WPI pkt len from hw, *packet_size=%u.\n",
		  __FUNCTION__, *packet_size);

	if (buf_size < *packet_size) {
		DbgPrintF(MP_WARN,
			  "%s - too small buf_size=%u, WPI pkt len is %u.\n",
			  __FUNCTION__, buf_size, *packet_size);
		data_len = buf_size;
	} else {
		data_len = *packet_size;
	}

	/* prepare to read packet data by write wpi_mode to 2 */
	val_wpi_crtl0 = FXGMAC_SET_REG_BITS(val_wpi_crtl0,
					    MGMT_WPI_CTRL0_WPI_MODE_POS,
					    MGMT_WPI_CTRL0_WPI_MODE_LEN,
					    MGMT_WPI_CTRL0_WPI_MODE_RD);
	writereg(pdata->pAdapter, val_wpi_crtl0,
		 pdata->base_mem + MGMT_WPI_CTRL0);

	dw_wpi_buf = (u32 *)wpi_buf;
	data_len_dw = (data_len + 3) / 4;

	i = 0;
	DbgPrintF(
		MP_TRACE,
		"%s - before retrieve, len=%d, len_dw=%d, reg_wpi_ctrl0=%08x.\n",
		__FUNCTION__, data_len, data_len_dw, val_wpi_crtl0);
	while ((0 == (val_wpi_crtl0 & MGMT_WPI_CTRL0_WPI_OP_DONE))) {
		if (i < data_len_dw) {
			regval = (u32)readreg(pdata->pAdapter,
					      pdata->base_mem +
						      MGMT_WPI_CTRL1_DATA);
			/*dw_wpi_buf[i] = SWAP_BYTES_32(regval);*/
			dw_wpi_buf[i] = regval;
		} else {
			break;
		}

		val_wpi_crtl0 = (u32)readreg(pdata->pAdapter,
					     pdata->base_mem + MGMT_WPI_CTRL0);
		i++;
	}
	if (*packet_size <= MAC_CRC_LENGTH) {
		DbgPrintF(MP_TRACE,
			  "%s - Warning, WPI pkt len is less 4 from hw.\n",
			  __FUNCTION__);
		return;
	}
	*packet_size -= MAC_CRC_LENGTH;

	/* once read data complete and write wpi_mode to 0, normal */
	val_wpi_crtl0 = FXGMAC_SET_REG_BITS(val_wpi_crtl0,
					    MGMT_WPI_CTRL0_WPI_MODE_POS,
					    MGMT_WPI_CTRL0_WPI_MODE_LEN,
					    MGMT_WPI_CTRL0_WPI_MODE_NORMAL);
	writereg(pdata->pAdapter, val_wpi_crtl0,
		 pdata->base_mem + MGMT_WPI_CTRL0);

	DbgPrintF(
		MP_TRACE,
		"%s - WPI done and back to normal mode, reg=%08x, read data=%dB.\n",
		__FUNCTION__, val_wpi_crtl0, i * 4);

	return;
}
#endif /* FUXI_PM_WPI_READ_FEATURE_EN */

static int fxgmac_enable_wake_link_change(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_LINKCHG_EN_POS,
				     WOL_LINKCHG_EN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);
	return 0;
}
static int fxgmac_disable_wake_link_change(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL);
	regval = FXGMAC_SET_REG_BITS(regval, WOL_LINKCHG_EN_POS,
				     WOL_LINKCHG_EN_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL);
	return 0;
}

static void fxgmac_config_wol(struct fxgmac_pdata *pdata, int en)
{
	/* enable or disable WOL. this function only set wake-up type, and power related configure
	 * will be in other place, see power management.
	 */
	if (!pdata->hw_feat.rwk) {
		netdev_err(pdata->netdev,
			   "error configuring WOL - not supported.\n");
		return;
	}

	fxgmac_disable_wake_magic_pattern(pdata);
	fxgmac_disable_wake_pattern(pdata);
	fxgmac_disable_wake_link_change(pdata);

	if (en) {
		/* config mac address for rx of magic or ucast */
		fxgmac_set_mac_address(pdata, (u8 *)(pdata->netdev->dev_addr));

		/* Enable Magic packet */
		if (pdata->expansion.wol & WAKE_MAGIC) {
			fxgmac_enable_wake_magic_pattern(pdata);
		}

		/* Enable global unicast packet */
		if (pdata->expansion.wol & WAKE_UCAST ||
		    pdata->expansion.wol & WAKE_MCAST ||
		    pdata->expansion.wol & WAKE_BCAST ||
		    pdata->expansion.wol & WAKE_ARP) {
			fxgmac_enable_wake_pattern(pdata);
		}

		/* Enable ephy link change */
		if ((FXGMAC_WOL_UPON_EPHY_LINK) &&
		    (pdata->expansion.wol & WAKE_PHY)) {
			fxgmac_enable_wake_link_change(pdata);
		}
	}
	device_set_wakeup_enable(/*pci_dev_to_dev*/ (pdata->dev), en);

	DPRINTK("config_wol callout\n");
}

static int fxgmac_get_ephy_state(struct fxgmac_pdata *pdata)
{
	u32 value;
	value = readreg(pdata->pAdapter, pdata->base_mem + MGMT_EPHY_CTRL);
	return value;
}

static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata)
{
#ifndef DPDK
	unsigned int dma_ch_isr, dma_ch_ier;
	struct fxgmac_channel *channel;
	unsigned int i;

#ifdef NIC_NET_ADAPETERCX
	u32 regval;
	/* config interrupt to level signal */
	regval = (u32)readreg(pdata->pAdapter, pdata->mac_regs + DMA_MR);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_INTM_POS, DMA_MR_INTM_LEN,
				     1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_QUREAD_POS,
				     DMA_MR_QUREAD_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_MR);
#endif

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		/* Clear all the interrupts which are set */
		dma_ch_isr = readreg(pdata->pAdapter,
				     FXGMAC_DMA_REG(channel, DMA_CH_SR));
		writereg(pdata->pAdapter, dma_ch_isr,
			 FXGMAC_DMA_REG(channel, DMA_CH_SR));

		/* Clear all interrupt enable bits */
		dma_ch_ier = 0;

		/* Enable following interrupts
		 *   NIE  - Normal Interrupt Summary Enable
		 *   AIE  - Abnormal Interrupt Summary Enable
		 *   FBEE - Fatal Bus Error Enable
		 */
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS,
						 DMA_CH_IER_NIE_LEN, 1);
		/* dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
		 * DMA_CH_IER_AIE_POS, DMA_CH_IER_AIE_LEN, 1);
		 */
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_FBEE_POS,
						 DMA_CH_IER_FBEE_LEN, 1);

		if (channel->tx_ring) {
			/* Enable the following Tx interrupts
			 *   TIE  - Transmit Interrupt Enable (unless using
			 *          per channel interrupts)
			 */
			if (!pdata->per_channel_irq)
				dma_ch_ier = FXGMAC_SET_REG_BITS(
					dma_ch_ier, DMA_CH_IER_TIE_POS,
					DMA_CH_IER_TIE_LEN, 1);
			if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) {
				if (pdata->per_channel_irq) {
					dma_ch_ier = FXGMAC_SET_REG_BITS(
						dma_ch_ier, DMA_CH_IER_TIE_POS,
						DMA_CH_IER_TIE_LEN, 1);
				}
			}
		}
		if (channel->rx_ring) {
			/* Enable following Rx interrupts
			 *   RBUE - Receive Buffer Unavailable Enable
			 *   RIE  - Receive Interrupt Enable (unless using
			 *          per channel interrupts)
			 */
			dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
							 DMA_CH_IER_RBUE_POS,
							 DMA_CH_IER_RBUE_LEN,
							 1);
			dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
							 DMA_CH_IER_RIE_POS,
							 DMA_CH_IER_RIE_LEN, 1);
		}

		writereg(pdata->pAdapter, dma_ch_ier,
			 FXGMAC_DMA_REG(channel, DMA_CH_IER));
	}
#else
	struct fxgmac_tx_queue *txq;
	unsigned int dma_ch_isr, dma_ch_ier;
	unsigned int i;

	for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) {
		txq = pdata->expansion.eth_dev->data->tx_queues[i];

		/* Clear all the interrupts which are set */
		dma_ch_isr = FXGMAC_DMA_IOREAD(txq, DMA_CH_SR);
		FXGMAC_DMA_IOWRITE(txq, DMA_CH_SR, dma_ch_isr);

		/* Clear all interrupt enable bits */
		dma_ch_ier = 0;

		/* Enable following interrupts
		 *   NIE  - Normal Interrupt Summary Enable
		 *   AIE  - Abnormal Interrupt Summary Enable
		 *   FBEE - Fatal Bus Error Enable
		 */
		FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1); /* 0  fx 1 */
		FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1);
		FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1);

		/* Enable following Rx interrupts
		 *   RBUE - Receive Buffer Unavailable Enable
		 *   RIE  - Receive Interrupt Enable (unless using
		 *          per channel interrupts in edge triggered
		 *          mode)
		 */
		FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); /* 0 fx 1 */
		FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); /* 0 fx 1 */

		FXGMAC_DMA_IOWRITE(txq, DMA_CH_IER, dma_ch_ier);
	}
#endif
}

static void fxgmac_enable_mtl_interrupts(struct fxgmac_pdata *pdata)
{
	unsigned int q_count, i;
	unsigned int mtl_q_isr;

	q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt);
	for (i = 0; i < q_count; i++) {
		/* Clear all the interrupts which are set */
		mtl_q_isr = readreg(pdata->pAdapter,
				    FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR));
		writereg(pdata->pAdapter, mtl_q_isr,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR));

		/* No MTL interrupts to be enabled */
		writereg(pdata->pAdapter, 0,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_IER));
	}
}

static void fxgmac_enable_mac_interrupts(struct fxgmac_pdata *pdata)
{
	unsigned int mac_ier = 0;
	u32 regval;

	/* Enable Timestamp interrupt */
	mac_ier = FXGMAC_SET_REG_BITS(mac_ier, MAC_IER_TSIE_POS,
				      MAC_IER_TSIE_LEN, 1);

	writereg(pdata->pAdapter, mac_ier, pdata->mac_regs + MAC_IER);

	/* Enable all counter interrupts */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RIER);
	regval = FXGMAC_SET_REG_BITS(regval, MMC_RIER_ALL_INTERRUPTS_POS,
				     MMC_RIER_ALL_INTERRUPTS_LEN, 0xffffffff);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_RIER);
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_TIER);
	regval = FXGMAC_SET_REG_BITS(regval, MMC_TIER_ALL_INTERRUPTS_POS,
				     MMC_TIER_ALL_INTERRUPTS_LEN, 0xffffffff);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_TIER);
}

static int fxgmac_set_fxgmii_2500_speed(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN,
				     pdata->phy_duplex);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

static int fxgmac_set_fxgmii_1000_speed(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN,
				     pdata->phy_duplex);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

static int fxgmac_set_fxgmii_100_speed(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN,
				     pdata->phy_duplex);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

static int fxgmac_set_fxgmii_10_speed(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN,
				     pdata->phy_duplex);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	return 0;
}

/**
 * fxgmac_check_phy_link - Get link/speed status
 * @pdata: pointer to gmac structure
 * @speed: pointer to link speed
 * @link_up: true is link is up, false otherwise
 * @link_up_wait_to_complete: bool used to wait for link up or not
 *
 * Reads the links register to determine if link is up and the current speed
 **/
static int fxgmac_check_phy_link(struct fxgmac_pdata *pdata, u32 *speed,
				 bool *link_up, bool link_up_wait_to_complete)
{
	u16 link_reg = 0;

	struct net_device *netdev = pdata->netdev;
	if (netdev->base_addr) {
		link_reg =
			(u16)(*((u32 *)(netdev->base_addr + MGMT_EPHY_CTRL)));

		/*
		 * check register address 0x1004
		 * b[6:5]       ephy_pause
		 * b[4:3]       ephy_speed 0b10 1000m 0b01 100m
		 * b[2]         ephy_duplex
		 * b[1]         ephy_link
		 * b[0]         ephy_reset. should be set to 1 before use phy.
		 */
		*link_up = false;
		if (link_reg & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) {
			if (link_up) {
				*link_up = (link_reg &
					    MGMT_EPHY_CTRL_STA_EPHY_LINKUP) ?
						   true :
						   false;
			}
			if (speed)
				*speed = (link_reg &
					  MGMT_EPHY_CTRL_STA_SPEED_MASK) >>
					 MGMT_EPHY_CTRL_STA_SPEED_POS;
		} else {
			DPRINTK("fxgmac_check_phy_link ethernet PHY not released.\n");
			return -1;
		}
	} else {
		DPRINTK("fxgmac_check_phy_link null base addr err\n");
		return -1;
	}

	return 0;
}

static int fxgmac_config_mac_speed(struct fxgmac_pdata *pdata)
{
	switch (pdata->phy_speed) {
	case SPEED_2500:
		fxgmac_set_fxgmii_2500_speed(pdata);
		break;
	case SPEED_1000:
		fxgmac_set_fxgmii_1000_speed(pdata);
		break;
	case SPEED_100:
		fxgmac_set_fxgmii_100_speed(pdata);
		break;
	case SPEED_10:
		fxgmac_set_fxgmii_10_speed(pdata);
		break;
	}
	return 0;
}

static int fxgmac_write_ephy_reg(struct fxgmac_pdata *pdata, u32 reg_id,
				 u32 data)
{
	u32 regval;
	u32 mdioctrl = reg_id * 0x10000 + 0x8000205;
	int busy = 15;

	writereg(pdata->pAdapter, data, pdata->mac_regs + MAC_MDIO_DATA);
	writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS);
	do {
		regval = readreg(pdata->pAdapter,
				 pdata->mac_regs + MAC_MDIO_ADDRESS);
		busy--;
	} while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy));

	DPRINTK("fxgmac_write_ephy_reg id %d %s, ctrl=0x%08x, data=0x%08x\n",
		reg_id, (regval & 0x1) ? "err" : "ok", regval, data);

	return (regval & MAC_MDIO_ADDRESS_BUSY) ? -1 : 0; /* -1 indicates err */
}

static int fxgmac_read_ephy_reg(struct fxgmac_pdata *pdata, u32 reg_id,
				u32 *data)
{
	u32 regval = 0, regret;
	u32 mdioctrl = reg_id * 0x10000 + 0x800020d;
	int busy = 15;

	writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS);
	do {
		regval = readreg(pdata->pAdapter,
				 pdata->mac_regs + MAC_MDIO_ADDRESS);
		busy--;
	} while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy));

	if (0 == (regval & MAC_MDIO_ADDRESS_BUSY)) {
		regret = readreg(pdata->pAdapter,
				 pdata->mac_regs + MAC_MDIO_DATA);
		if (data)
			*data = regret;
		return regret;
	}

	DPRINTK("fxgmac_read_ephy_reg id=0x%02x err, busy=%d, ctrl=0x%08x.\n",
		reg_id, busy, regval);
	return -1;
}

static int fxgmac_write_ephy_mmd_reg(struct fxgmac_pdata *pdata, u32 reg_id,
				     u32 mmd, u32 data)
{
	u32 regval;
	u32 mdioctrl = (mmd << 16) + 0x8000207;
	u32 regdata = (reg_id << 16) + data;
	/* for phy mmd reg r/w operation, set more delay time than phy mii reg r/w */
	int busy = 60;

	writereg(pdata->pAdapter, regdata, pdata->mac_regs + MAC_MDIO_DATA);
	writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS);
	do {
		regval = readreg(pdata->pAdapter,
				 pdata->mac_regs + MAC_MDIO_ADDRESS);
		busy--;
	} while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy));

	DPRINTK("fxgmac_write_ephy_mmd_reg id %d mmd %d %s, ctrl=0x%08x, data=0x%08x\n",
		reg_id, mmd, (regval & 0x1) ? "err" : "ok", regval, data);

	return (regval & MAC_MDIO_ADDRESS_BUSY) ? -1 : 0; /* -1 indicates err */
}

static void fxgmac_config_flow_control(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;

	fxgmac_config_tx_flow_control(pdata);
	fxgmac_config_rx_flow_control(pdata);

	fxgmac_read_ephy_reg(pdata, REG_MII_ADVERTISE, &regval);
	/* set auto negotiation advertisement pause ability */
	if (pdata->tx_pause || pdata->rx_pause) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_PAUSE_POS,
					     PHY_MII_ADVERTISE_PAUSE_LEN, 1);
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_ASYPAUSE_POS,
					     PHY_MII_ADVERTISE_ASYPAUSE_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_PAUSE_POS,
					     PHY_MII_ADVERTISE_PAUSE_LEN, 0);
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_ASYPAUSE_POS,
					     PHY_MII_ADVERTISE_ASYPAUSE_LEN, 0);
	}
	fxgmac_write_ephy_reg(pdata, REG_MII_ADVERTISE, regval);
	/* after change the auto negotiation advertisement need to soft reset */
	fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN,
				     1);
	fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);
}

static int fxgmac_set_ephy_autoneg_advertise(struct fxgmac_pdata *pdata,
					     struct fxphy_ag_adv phy_ag_adv)
{
	u32 regval = 0, ret = 0;

	if (phy_ag_adv.auto_neg_en) {
		fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
		regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS,
					     PHY_CR_AUTOENG_LEN, 1);
		ret |= fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);
	} else {
		fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
		regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS,
					     PHY_CR_AUTOENG_LEN, 0);
		ret |= fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);
	}

	fxgmac_read_ephy_reg(pdata, REG_MII_CTRL1000, &regval);
	if (phy_ag_adv.full_1000m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_CTRL1000_1000FULL_POS,
					     PHY_MII_CTRL1000_1000FULL_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_CTRL1000_1000FULL_POS,
					     PHY_MII_CTRL1000_1000FULL_LEN, 0);
	}
	if (phy_ag_adv.half_1000m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_CTRL1000_1000HALF_POS,
					     PHY_MII_CTRL1000_1000HALF_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_CTRL1000_1000HALF_POS,
					     PHY_MII_CTRL1000_1000HALF_LEN, 0);
	}
	ret |= fxgmac_write_ephy_reg(pdata, REG_MII_CTRL1000, regval);

	fxgmac_read_ephy_reg(pdata, REG_MII_ADVERTISE, &regval);

	if (phy_ag_adv.full_100m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_100FULL_POS,
					     PHY_MII_ADVERTISE_100FULL_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_100FULL_POS,
					     PHY_MII_ADVERTISE_100FULL_LEN, 0);
	}
	if (phy_ag_adv.half_100m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_100HALF_POS,
					     PHY_MII_ADVERTISE_100HALF_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_100HALF_POS,
					     PHY_MII_ADVERTISE_100HALF_LEN, 0);
	}
	if (phy_ag_adv.full_10m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_10FULL_POS,
					     PHY_MII_ADVERTISE_10FULL_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_10FULL_POS,
					     PHY_MII_ADVERTISE_10FULL_LEN, 0);
	}
	if (phy_ag_adv.half_10m) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_10HALF_POS,
					     PHY_MII_ADVERTISE_10HALF_LEN, 1);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PHY_MII_ADVERTISE_10HALF_POS,
					     PHY_MII_ADVERTISE_10HALF_LEN, 0);
	}

	ret |= fxgmac_write_ephy_reg(pdata, REG_MII_ADVERTISE, regval);
	/* after change the auto negotiation advertisement need to soft reset */
	fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN,
				     1);
	ret |= fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);

	return ret;
}

static int fxgmac_phy_config(struct fxgmac_pdata *pdata)
{
	struct fxphy_ag_adv phy_ag_adv;

	if (pdata->phy_autoeng) {
		phy_ag_adv.auto_neg_en = 1;
	} else {
		phy_ag_adv.auto_neg_en = 0;
	}
	switch (pdata->phy_speed) {
	case SPEED_1000:
		phy_ag_adv.full_1000m = 1, phy_ag_adv.half_1000m = 0,
		phy_ag_adv.full_100m = 1, phy_ag_adv.half_100m = 1,
		phy_ag_adv.full_10m = 1, phy_ag_adv.half_10m = 1;
		break;

	case SPEED_100:
		phy_ag_adv.full_1000m = 0, phy_ag_adv.half_1000m = 0;
		if (pdata->phy_duplex) {
			phy_ag_adv.full_100m = 1;
		} else {
			phy_ag_adv.full_100m = 0;
		}
		phy_ag_adv.half_100m = 1, phy_ag_adv.full_10m = 1,
		phy_ag_adv.half_10m = 1;
		break;

	case SPEED_10:
		phy_ag_adv.full_1000m = 0, phy_ag_adv.half_1000m = 0;
		phy_ag_adv.full_100m = 0, phy_ag_adv.half_100m = 0;
		if (pdata->phy_duplex) {
			phy_ag_adv.full_10m = 1;
		} else {
			phy_ag_adv.full_10m = 0;
		}
		phy_ag_adv.half_10m = 1;
		break;

	default:
		break;
	}
	return fxgmac_set_ephy_autoneg_advertise(pdata, phy_ag_adv);
}

static void fxgmac_phy_green_ethernet(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;
	/* GREEN */
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_REG_PMA_DBG0_ADC);
	fxgmac_write_ephy_reg(
		pdata, REG_MII_EXT_DATA,
		REG_MII_EXT_ENABLE_GIGA_POWER_SAVING_FOR_SHORT_CABLE);

	/* CLD */
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_REG_CLD_REG0);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_ENABLE_CLD_NP_WP);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_REG_CLD_REG1);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_ENABLE_CLD_GT_HT_BT);

	/* after change green ethernet & CLD need to soft reset */
	fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN,
				     1);
	fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);
}

static void fxgmac_phy_eee_feature(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + DMA_SBMR);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_EN_LPI_POS,
				     DMA_SBMR_EN_LPI_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_LPI_XIT_PKT_POS,
				     DMA_SBMR_LPI_XIT_PKT_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_AALE_POS,
				     DMA_SBMR_AALE_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_SBMR);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIATE_POS, MAC_LPIATE_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_LPITXA_POS, MAC_LPITXA_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PLS_POS, MAC_PLS_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIEN_POS, MAC_LPIEN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_STA);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_TIMER);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIET_POS, MAC_LPIET_LEN,
				     MAC_LPI_ENTRY_TIMER);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_TIMER);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_CONTROL);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_TWT_POS, MAC_TWT_LEN,
				     MAC_TWT_TIMER);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_LST_POS, MAC_LST_LEN,
				     MAC_LST_TIMER);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_CONTROL);

	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_MS_TIC_COUNTER);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_MS_TIC_POS, MAC_MS_TIC_LEN,
				     MAC_MS_TIC);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_MS_TIC_COUNTER);

	fxgmac_write_ephy_mmd_reg(pdata, REG_MMD_EEE_ABILITY_REG, 0x07,
				  REG_MMD_EEE_ABILITY_VALUE);

	/* after change EEE need to soft reset */
	fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN,
				     1);
	fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval);
}

static void fxgmac_reset_phy(struct fxgmac_pdata *pdata)
{
	u32 value = 0;

	value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS,
				    MGMT_EPHY_CTRL_RESET_LEN,
				    MGMT_EPHY_CTRL_STA_EPHY_RESET);
	writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL);
	usleep_range_ex(pdata->pAdapter, 1500, 1500);
}

void fxgmac_release_phy(struct fxgmac_pdata *pdata)
{
	u32 value = 0;

	value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS,
				    MGMT_EPHY_CTRL_RESET_LEN,
				    MGMT_EPHY_CTRL_STA_EPHY_RELEASE);
	writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL);
	usleep_range_ex(pdata->pAdapter, 100, 150);
	value = readreg(pdata->pAdapter, pdata->base_mem + MGMT_EPHY_CTRL);
	DBGPRINT(MP_LOUD, ("0x1004: 0x%x\n", value));
#ifdef AISC_MODE
	fxgmac_read_ephy_reg(pdata, REG_MII_SPEC_CTRL,
			     &value); /* read  phy specific control */
	value = FXGMAC_SET_REG_BITS(value, PHY_MII_SPEC_CTRL_CRS_ON_POS,
				    PHY_MII_SPEC_CTRL_CRS_ON_LEN,
				    1); /* set on crs on */
	fxgmac_write_ephy_reg(pdata, REG_MII_SPEC_CTRL,
			      value); /* phy specific control set on crs on */

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_ANALOG_CFG3);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value);
	/* VGA bandwidth, default is 2 after reset. Set to 0 to mitigate unstable issue in 130m. */
	value = FXGMAC_SET_REG_BITS(value,
				    MII_EXT_ANALOG_CFG3_ADC_START_CFG_POS,
				    MII_EXT_ANALOG_CFG3_ADC_START_CFG_LEN,
				    MII_EXT_ANALOG_CFG3_ADC_START_CFG_DEFAULT);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value);

	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &value);
	/* led index use bit0~bit5 */
	value = FXGMAC_GET_REG_BITS(value, EFUSE_LED_POS, EFUSE_LED_LEN);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_ANALOG_CFG2);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_ANALOG_CFG2_LED_VALUE);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_ANALOG_CFG8);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_ANALOG_CFG8_LED_VALUE);

	if (EFUSE_LED_COMMON_SOLUTION != value) {
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		switch (value) {
		case EFUSE_LED_SOLUTION1:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION1);
			break;
		case EFUSE_LED_SOLUTION2:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION2);
			break;
		case EFUSE_LED_SOLUTION3:
		case EFUSE_LED_SOLUTION4:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION3);
			break;
		default:
			/* default solution */
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION0);
			break;
		}

		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		switch (value) {
		case EFUSE_LED_SOLUTION1:
		case EFUSE_LED_SOLUTION4:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION1);
			break;
		case EFUSE_LED_SOLUTION2:
		case EFUSE_LED_SOLUTION3:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION2);
			break;
		default:
			/* default solution */
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION0);
			break;
		}

		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		switch (value) {
		case EFUSE_LED_SOLUTION1:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION0);
			break;
		case EFUSE_LED_SOLUTION2:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION2);
			break;
		case EFUSE_LED_SOLUTION3:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION3);
			break;
		case EFUSE_LED_SOLUTION4:
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION4);
			break;
		default:
			/* default solution */
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION0);
			break;
		}

		if (EFUSE_LED_SOLUTION2 == value) {
			fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
					      REG_MII_EXT_COMMON_LED_BLINK_CFG);
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED_BLINK_CFG_SOLUTION2);
		}
	}
#endif
}

static void fxgmac_enable_phy_check(struct fxgmac_pdata *pdata)
{
	u32 value = 0;

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value);
	value = FXGMAC_SET_REG_BITS(value, REG_MII_EXT_PKG_CHECK_POS,
				    REG_MII_EXT_PKG_CHECK_LEN,
				    REG_MII_EXT_PKG_ENABLE_CHECK);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value);
}

static void fxgmac_disable_phy_check(struct fxgmac_pdata *pdata)
{
	u32 value = 0;

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value);
	value = FXGMAC_SET_REG_BITS(value, REG_MII_EXT_PKG_CHECK_POS,
				    REG_MII_EXT_PKG_CHECK_LEN,
				    REG_MII_EXT_PKG_DISABLE_CHECK);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value);
}

static void fxgmac_setup_cable_loopback(struct fxgmac_pdata *pdata)
{
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_SLEEP_REG_ENABLE_LOOPBACK);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_LPBK_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_LPBK_REG_ENABLE_LOOPBACK);

	fxgmac_write_ephy_reg(pdata, REG_MII_BMCR,
			      REG_MII_BMCR_ENABLE_LOOPBACK);
}

static void fxgmac_clean_cable_loopback(struct fxgmac_pdata *pdata)
{
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_SLEEP_REG_CLEAN_LOOPBACK);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_LPBK_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
			      REG_MII_EXT_LPBK_REG_CLEAN_LOOPBACK);

	fxgmac_write_ephy_reg(pdata, REG_MII_BMCR,
			      REG_MII_BMCR_DISABLE_LOOPBACK);
}

static void fxgmac_disable_phy_sleep(struct fxgmac_pdata *pdata)
{
	u32 value = 0;
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value);

	value = FXGMAC_SET_REG_BITS(value, MII_EXT_SLEEP_CONTROL1_EN_POS,
				    MII_EXT_SLEEP_CONTROL1_EN_LEN, 0);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value);
}

static void fxgmac_enable_phy_sleep(struct fxgmac_pdata *pdata)
{
	u32 value = 0;
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value);

	value = FXGMAC_SET_REG_BITS(value, MII_EXT_SLEEP_CONTROL1_EN_POS,
				    MII_EXT_SLEEP_CONTROL1_EN_LEN, 1);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL_REG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value);
}

static void fxgmac_close_phy_led(struct fxgmac_pdata *pdata)
{
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_COMMON_LED0_CFG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_COMMON_LED1_CFG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_COMMON_LED2_CFG);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);
}

static void fxmgac_config_led_under_active(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;
	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &regval);
	/* led index use bit0~bit5 */
	regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN);
	if (EFUSE_LED_COMMON_SOLUTION == regval) {
		DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s0_led_setting[0]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s0_led_setting[1]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s0_led_setting[2]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s0_led_setting[3]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_BLINK_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s0_led_setting[4]);
	}
}

static void fxgmac_config_led_under_sleep(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;
	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &regval);
	/* led index use bit0~bit5 */
	regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN);
	if (EFUSE_LED_COMMON_SOLUTION == regval) {
		DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s3_led_setting[0]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s3_led_setting[1]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s3_led_setting[2]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s3_led_setting[3]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_BLINK_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s3_led_setting[4]);
	}
}

static void fxgmac_config_led_under_shutdown(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;
	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &regval);
	/* led index use bit0~bit5 */
	regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN);
	if (EFUSE_LED_COMMON_SOLUTION == regval) {
		DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s5_led_setting[0]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s5_led_setting[1]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s5_led_setting[2]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s5_led_setting[3]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_BLINK_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.s5_led_setting[4]);
	}
}

static void fxgmac_config_led_under_disable(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;
	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &regval);
	/* led index use bit0~bit5 */
	regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN);
	if (EFUSE_LED_COMMON_SOLUTION == regval) {
		DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.disable_led_setting[0]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.disable_led_setting[1]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.disable_led_setting[2]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.disable_led_setting[3]);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED_BLINK_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA,
				      pdata->led.disable_led_setting[4]);
	} else {
		/* http://redmine.motor-comm.com/issues/4101 */
		/* for disable case, reset phy to close LED */
		fxgmac_reset_phy(pdata);
	}
}

extern void fxgmac_diag_get_rx_info(struct fxgmac_channel *channel);

static int fxgmac_dev_read(struct fxgmac_channel *channel)
{
	struct fxgmac_pdata *pdata = channel->pdata;
	struct fxgmac_ring *ring = channel->rx_ring;
	struct net_device *netdev = pdata->netdev;
	struct fxgmac_desc_data *desc_data;
	struct fxgmac_dma_desc *dma_desc;
	struct fxgmac_pkt_info *pkt_info;
	unsigned int err, etlt, l34t;

	static unsigned int cnt_incomplete;

	desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur);
	dma_desc = desc_data->dma_desc;
	pkt_info = &ring->pkt_info;

	/* Check for data availability */
	if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_OWN_POS,
				   RX_NORMAL_DESC3_OWN_LEN)) {
		return 1;
	}

	/* Make sure descriptor fields are read after reading the OWN bit */
	dma_rmb();

	if (netif_msg_rx_status(pdata))
		fxgmac_dump_rx_desc(pdata, ring, ring->cur);

	if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_CTXT_POS,
				   RX_NORMAL_DESC3_CTXT_LEN)) {
		/* Timestamp Context Descriptor */
		fxgmac_get_rx_tstamp(pkt_info, dma_desc);

		pkt_info->attributes = FXGMAC_SET_REG_BITS(
			pkt_info->attributes, RX_PACKET_ATTRIBUTES_CONTEXT_POS,
			RX_PACKET_ATTRIBUTES_CONTEXT_LEN, 1);
		pkt_info->attributes = FXGMAC_SET_REG_BITS(
			pkt_info->attributes,
			RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS,
			RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN, 0);
		if (netif_msg_rx_status(pdata))
			DPRINTK("dev_read context desc, ch=%s\n", channel->name);
		return 0;
	}

	/* Normal Descriptor, be sure Context Descriptor bit is off */
	pkt_info->attributes = FXGMAC_SET_REG_BITS(
		pkt_info->attributes, RX_PACKET_ATTRIBUTES_CONTEXT_POS,
		RX_PACKET_ATTRIBUTES_CONTEXT_LEN, 0);

	/* Get the header length */
	if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_FD_POS,
				   RX_NORMAL_DESC3_FD_LEN)) {
		desc_data->rx.hdr_len = FXGMAC_GET_REG_BITS_LE(
			dma_desc->desc2, RX_NORMAL_DESC2_HL_POS,
			RX_NORMAL_DESC2_HL_LEN);
		if (desc_data->rx.hdr_len)
			pdata->stats.rx_split_header_packets++;
	}
	l34t = 0;

	/* Get the pkt_info length */
	desc_data->rx.len = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3,
						   RX_NORMAL_DESC3_PL_POS,
						   RX_NORMAL_DESC3_PL_LEN);

	if (!FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_LD_POS,
				    RX_NORMAL_DESC3_LD_LEN)) {
		/* Not all the data has been transferred for this pkt_info */
		pkt_info->attributes = FXGMAC_SET_REG_BITS(
			pkt_info->attributes,
			RX_PACKET_ATTRIBUTES_INCOMPLETE_POS,
			RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, 1);
		cnt_incomplete++;
		if ((cnt_incomplete < 2) && netif_msg_rx_status(pdata))
			DPRINTK("dev_read NOT last desc, pkt incomplete yet,%u\n",
				cnt_incomplete);

		return 0;
	}
	if ((cnt_incomplete) && netif_msg_rx_status(pdata))
		DPRINTK("dev_read rx back to normal and incomplete cnt=%u\n",
			cnt_incomplete);
	cnt_incomplete = 0; /* when back to normal, reset cnt */

	/* This is the last of the data for this pkt_info */
	pkt_info->attributes = FXGMAC_SET_REG_BITS(
		pkt_info->attributes, RX_PACKET_ATTRIBUTES_INCOMPLETE_POS,
		RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, 0);

	/* Set checksum done indicator as appropriate */
	if (netdev->features & NETIF_F_RXCSUM)
		pkt_info->attributes = FXGMAC_SET_REG_BITS(
			pkt_info->attributes,
			RX_PACKET_ATTRIBUTES_CSUM_DONE_POS,
			RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN, 1);

	/* Check for errors (only valid in last descriptor) */
	err = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_ES_POS,
				     RX_NORMAL_DESC3_ES_LEN);
	etlt = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_ETLT_POS,
				      RX_NORMAL_DESC3_ETLT_LEN);
	if ((err) && netif_msg_rx_status(pdata)) {
		DPRINTK("dev_read:head_len=%u, pkt_len=%u, err=%u, etlt=%#x, desc2=0x%08x, desc3=0x%08x\n",
			desc_data->rx.hdr_len, desc_data->rx.len, err, etlt,
			dma_desc->desc2, dma_desc->desc3);
	}

	if (!err || !etlt) {
		/* No error if err is 0 or etlt is 0 */
		if ((etlt == 0x4 /*yzhang changed to 0x4, 0x09*/) &&
		    (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) {
			pkt_info->attributes = FXGMAC_SET_REG_BITS(
				pkt_info->attributes,
				RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
				RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, 1);
			pkt_info->vlan_ctag = FXGMAC_GET_REG_BITS_LE(
				dma_desc->desc0, RX_NORMAL_DESC0_OVT_POS,
				RX_NORMAL_DESC0_OVT_LEN);
			netif_dbg(pdata, rx_status, netdev, "vlan-ctag=%#06x\n",
				  pkt_info->vlan_ctag);
		}
	} else {
		if (etlt == 0x05 || etlt == 0x06)
			pkt_info->attributes = FXGMAC_SET_REG_BITS(
				pkt_info->attributes,
				RX_PACKET_ATTRIBUTES_CSUM_DONE_POS,
				RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN, 0);
		else
			pkt_info->errors = FXGMAC_SET_REG_BITS(
				pkt_info->errors, RX_PACKET_ERRORS_FRAME_POS,
				RX_PACKET_ERRORS_FRAME_LEN, 1);
	}

	return 0;
}

static int fxgmac_enable_int(struct fxgmac_channel *channel,
			     enum fxgmac_int int_id)
{
	unsigned int dma_ch_ier;

	dma_ch_ier = readreg(channel->pdata->pAdapter,
			     FXGMAC_DMA_REG(channel, DMA_CH_IER));

	switch (int_id) {
	case FXGMAC_INT_DMA_CH_SR_TI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS,
						 DMA_CH_IER_TIE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_TPS:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_TXSE_POS,
						 DMA_CH_IER_TXSE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_TBU:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_TBUE_POS,
						 DMA_CH_IER_TBUE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_RI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS,
						 DMA_CH_IER_RIE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_RBU:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_RBUE_POS,
						 DMA_CH_IER_RBUE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_RPS:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RSE_POS,
						 DMA_CH_IER_RSE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_TI_RI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS,
						 DMA_CH_IER_TIE_LEN, 1);
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS,
						 DMA_CH_IER_RIE_LEN, 1);
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS,
						 DMA_CH_IER_NIE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_CH_SR_FBE:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_FBEE_POS,
						 DMA_CH_IER_FBEE_LEN, 1);
		break;
	case FXGMAC_INT_DMA_ALL:
		dma_ch_ier |= channel->saved_ier;
		break;
	default:
		return -1;
	}

	writereg(channel->pdata->pAdapter, dma_ch_ier,
		 FXGMAC_DMA_REG(channel, DMA_CH_IER));

	return 0;
}

static int fxgmac_disable_int(struct fxgmac_channel *channel,
			      enum fxgmac_int int_id)
{
	unsigned int dma_ch_ier;

	dma_ch_ier = readreg(channel->pdata->pAdapter,
			     FXGMAC_DMA_REG(channel, DMA_CH_IER));

	switch (int_id) {
	case FXGMAC_INT_DMA_CH_SR_TI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS,
						 DMA_CH_IER_TIE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_TPS:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_TXSE_POS,
						 DMA_CH_IER_TXSE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_TBU:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_TBUE_POS,
						 DMA_CH_IER_TBUE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_RI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS,
						 DMA_CH_IER_RIE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_RBU:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_RBUE_POS,
						 DMA_CH_IER_RBUE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_RPS:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RSE_POS,
						 DMA_CH_IER_RSE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_TI_RI:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS,
						 DMA_CH_IER_TIE_LEN, 0);
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS,
						 DMA_CH_IER_RIE_LEN, 0);
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS,
						 DMA_CH_IER_NIE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_CH_SR_FBE:
		dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier,
						 DMA_CH_IER_FBEE_POS,
						 DMA_CH_IER_FBEE_LEN, 0);
		break;
	case FXGMAC_INT_DMA_ALL:
		channel->saved_ier = dma_ch_ier & FXGMAC_DMA_INTERRUPT_MASK;
		dma_ch_ier &= ~FXGMAC_DMA_INTERRUPT_MASK;
		break;
	default:
		return -1;
	}

	writereg(channel->pdata->pAdapter, dma_ch_ier,
		 FXGMAC_DMA_REG(channel, DMA_CH_IER));

	return 0;
}

static int fxgmac_dismiss_DMA_int(struct fxgmac_channel *channel, int int_id)
{
	unsigned int dma_ch_ier;

	int_id = int_id;
	dma_ch_ier = readreg(channel->pdata->pAdapter,
			     FXGMAC_DMA_REG(channel, DMA_CH_SR /*1160*/));
	writereg(channel->pdata->pAdapter, dma_ch_ier,
		 FXGMAC_DMA_REG(channel, DMA_CH_SR));

	return 0;
}

static void fxgmac_dismiss_MTL_Q_int(struct fxgmac_pdata *pdata)
{
	unsigned int q_count, i;
	unsigned int mtl_q_isr;

	q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt);
	for (i = 0; i < q_count; i++) {
		/* Clear all the interrupts which are set */
		mtl_q_isr = readreg(pdata->pAdapter,
				    FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR));
		writereg(pdata->pAdapter, mtl_q_isr,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR));
	}
}

static int fxgmac_dismiss_MAC_int(struct fxgmac_pdata *pdata)
{
	u32 regval, regErrVal;

	/* all MAC interrupts in 0xb0 */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ISR);
	/* MAC tx/rx error interrupts in 0xb8 */
	regErrVal = readreg(pdata->pAdapter, pdata->mac_regs + MAC_TX_RX_STA);
	return 0;
}

static int fxgmac_dismiss_MAC_PMT_int(struct fxgmac_pdata *pdata)
{
	u32 regval;

	/* MAC PMT interrupts in 0xc0 */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	return 0;
}

static int fxgmac_dismiss_MAC_LPI_int(struct fxgmac_pdata *pdata)
{
	u32 regval;

	/* MAC PMT interrupts in 0xc0 */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_STA);

	return 0;
}

static int fxgmac_dismiss_MAC_DBG_int(struct fxgmac_pdata *pdata)
{
	u32 regval;

	/* MAC PMT interrupts in 0xc0 */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA);

	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_DBG_STA);

	return 0;
}

int fxgmac_dismiss_all_int(struct fxgmac_pdata *pdata)
{
	struct fxgmac_channel *channel;
	unsigned int i, regval;
	struct net_device *netdev = pdata->netdev;

	if (netif_msg_drv(pdata)) {
		DPRINTK("fxgmac_dismiss_all_int callin\n");
	}

	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		fxgmac_dismiss_DMA_int(channel, 0);
	}
	fxgmac_dismiss_MTL_Q_int(pdata);
	fxgmac_dismiss_MAC_int(pdata);
	fxgmac_dismiss_MAC_PMT_int(pdata);
	fxgmac_dismiss_MAC_LPI_int(pdata);
	fxgmac_dismiss_MAC_DBG_int(pdata);

	/* control module int to PCIe slot */
	if (netdev->base_addr) {
		regval = (unsigned int)(*(
			(u32 *)(netdev->base_addr + MGMT_INT_CTRL0)));
	}
	return 0;
}

static void fxgmac_set_interrupt_moderation(struct fxgmac_pdata *pdata)
{
	u32 value = 0, time;

	pdata->intr_mod_timer = INT_MOD_IN_US;

	time = (pdata->intr_mod) ? pdata->intr_mod_timer : 0;
	time = (pdata->intr_mod) ? pdata->tx_usecs : 0;
	value = FXGMAC_SET_REG_BITS(value, INT_MOD_TX_POS, INT_MOD_TX_LEN,
				    time);
	time = (pdata->intr_mod) ? pdata->rx_usecs : 0;
	value = FXGMAC_SET_REG_BITS(value, INT_MOD_RX_POS, INT_MOD_RX_LEN,
				    time);
	writereg(pdata->pAdapter, value, pdata->base_mem + INT_MOD);
}
static void fxgmac_enable_msix_rxtxinterrupt(struct fxgmac_pdata *pdata)
{
	u32 intid;

	for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) {
		writereg(pdata->pAdapter, 0,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}
}
static void fxgmac_disable_msix_interrupt(struct fxgmac_pdata *pdata)
{
	u32 intid;

	for (intid = 0; intid < MSIX_TBL_MAX_NUM; intid++) {
		writereg(pdata->pAdapter, 0x1,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}
}
static void fxgmac_enable_msix_rxtxphyinterrupt(struct fxgmac_pdata *pdata)
{
	u32 intid, regval = 0;
#if !(FUXI_EPHY_INTERRUPT_D0_OFF)
	struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
#endif

	for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) {
		writereg(pdata->pAdapter, 0,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}
	writereg(pdata->pAdapter, 0,
		 pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET +
			 MSI_ID_PHY_OTHER * 16);
#if !(FUXI_EPHY_INTERRUPT_D0_OFF)
	hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS,
			      NULL); /*  clear  phy interrupt */
	regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS,
				     PHY_INT_MASK_LINK_UP_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS,
				     PHY_INT_MASK_LINK_DOWN_LEN, 1);
	hw_ops->write_ephy_reg(
		pdata, REG_MII_INT_MASK,
		regval); /* enable phy interrupt    ASIC bit10 linkup  bit11 linkdown */
#endif
}
static void fxgmac_enable_msix_one_interrupt(struct fxgmac_pdata *pdata,
					     u32 intid)
{
	writereg(pdata->pAdapter, 0,
		 pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET +
			 intid * 16);
}

static void fxgmac_disable_msix_one_interrupt(struct fxgmac_pdata *pdata,
					      u32 intid)
{
	writereg(pdata->pAdapter, 0x01,
		 pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET +
			 intid * 16);
}

static bool fxgmac_enable_mgm_interrupt(struct fxgmac_pdata *pdata)
{
	writereg(pdata->pAdapter, 0xf0000000, pdata->base_mem + MGMT_INT_CTRL0);
	return true;
}

static bool fxgmac_disable_mgm_interrupt(struct fxgmac_pdata *pdata)
{
	writereg(pdata->pAdapter, 0xffff0000, pdata->base_mem + MGMT_INT_CTRL0);
	return true;
}

static int fxgmac_flush_tx_queues(struct fxgmac_pdata *pdata)
{
	unsigned int i, count;
	u32 regval;

	for (i = 0; i < pdata->tx_q_count; i++) {
		regval = readreg(pdata->pAdapter,
				 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_FTQ_POS,
					     MTL_Q_TQOMR_FTQ_LEN, 1);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
		DPRINTK("fxgmac_flush_tx_queues, reg=0x%p, val=0x%08x\n",
			FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR), regval);
	}

	for (i = 0; i < pdata->tx_q_count; i++) {
		count = 2000;
		do {
			usleep_range_ex(pdata->pAdapter, 40, 50);
			regval = readreg(pdata->pAdapter,
					 FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR));
			regval = FXGMAC_GET_REG_BITS(regval,
						     MTL_Q_TQOMR_FTQ_POS,
						     MTL_Q_TQOMR_FTQ_LEN);

		} while (--count && regval);
		DPRINTK("fxgmac_flush_tx_queues wait... reg=0x%p, val=0x%08x\n",
			FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR), regval);
		if (regval) { /*(!count)*/
			return -EBUSY;
		}
	}

	return 0;
}

static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata)
{
	u32 regval;

	regval = readreg(pdata->pAdapter, pdata->mac_regs + DMA_SBMR);
	/* Set enhanced addressing mode */
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_EAME_POS,
				     DMA_SBMR_EAME_LEN, 1);
	/* Set the System Bus mode */
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_FB_POS, DMA_SBMR_FB_LEN,
				     0);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_4_POS,
				     DMA_SBMR_BLEN_4_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_8_POS,
				     DMA_SBMR_BLEN_8_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_16_POS,
				     DMA_SBMR_BLEN_16_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_32_POS,
				     DMA_SBMR_BLEN_32_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_SBMR);
}

static void fxgmac_legacy_link_speed_setting(struct fxgmac_pdata *pdata)
{
	unsigned int i = 0, regval = 0;

	fxgmac_phy_config(pdata);
	for (i = 0, regval = fxgmac_get_ephy_state(pdata);
	     (!(regval & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) ||
	      !(regval & MGMT_EPHY_CTRL_STA_EPHY_LINKUP)) &&
	     (i < PHY_LINK_TIMEOUT);
	     regval = fxgmac_get_ephy_state(pdata), i++) {
		usleep_range_ex(pdata->pAdapter, 2000, 2000);
	}
	fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS,
			     NULL); /* clear  phy interrupt. */
}

static void fxgmac_pre_powerdown(struct fxgmac_pdata *pdata, bool phyloopback)
{
	unsigned int regval = 0;

	fxgmac_disable_rx(pdata);

	/* HERE, WE NEED TO CONSIDER PHY CONFIG...TBD */
	DPRINTK("fxgmac_config_powerdown, phy and mac status update\n");
	/* for phy cable loopback, it can't configure phy speed, it will cause os resume again by link change although it has finished speed setting, */
	if (!phyloopback) {
		/* When the Linux platform enters the s4 state, it goes through
		 * the suspend->resume->suspend process. The process of
		 * suspending again after resume is fast, and PHY
		 * auto-negotiation is not yet complete, so the
		 * auto-negotiation of PHY must be carried out again. When the
		 * Linux platform enters the s4 state, force speed to 10M.
		 */
		pdata->phy_speed = SPEED_10;
		fxgmac_legacy_link_speed_setting(pdata);
	}

	fxgmac_config_mac_speed(pdata);

	/* After enable OOB_WOL from efuse, mac will loopcheck phy status, and
	 * lead to panic sometimes. So we should disable it from powerup,
	 * enable it from power down.
	 */
	regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + OOB_WOL_CTRL);
	regval = FXGMAC_SET_REG_BITS(regval, OOB_WOL_CTRL_DIS_POS,
				     OOB_WOL_CTRL_DIS_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + OOB_WOL_CTRL);
	usleep_range_ex(pdata->pAdapter, 2000, 2000);

	/* after enable OOB_WOL, recofigure mac addr again */
	fxgmac_set_mac_address(pdata, pdata->mac_addr);
}

/* only supports four patterns, and patterns will be cleared on every call */
static void fxgmac_set_pattern_data(struct fxgmac_pdata *pdata)
{
	u32 ip_addr, i = 0;
	u8 type_offset, op_offset, tip_offset;
	struct pattern_packet packet;
	struct wol_bitmap_pattern
		pattern[4]; /* for WAKE_UCAST, WAKE_BCAST, WAKE_MCAST, WAKE_ARP. */

	memset(pattern, 0, sizeof(struct wol_bitmap_pattern) * 4);

	/* config ucast */
	if (pdata->expansion.wol & WAKE_UCAST) {
		pattern[i].mask_info[0] = 0x3F;
		pattern[i].mask_size = sizeof(pattern[0].mask_info);
		memcpy(pattern[i].pattern_info, pdata->mac_addr, ETH_ALEN);
		pattern[i].pattern_offset = 0;
		i++;
	}

	/* config bcast */
	if (pdata->expansion.wol & WAKE_BCAST) {
		pattern[i].mask_info[0] = 0x3F;
		pattern[i].mask_size = sizeof(pattern[0].mask_info);
		memset(pattern[i].pattern_info, 0xFF, ETH_ALEN);
		pattern[i].pattern_offset = 0;
		i++;
	}

	/* config mcast */
	if (pdata->expansion.wol & WAKE_MCAST) {
		pattern[i].mask_info[0] = 0x7;
		pattern[i].mask_size = sizeof(pattern[0].mask_info);
		pattern[i].pattern_info[0] = 0x1;
		pattern[i].pattern_info[1] = 0x0;
		pattern[i].pattern_info[2] = 0x5E;
		pattern[i].pattern_offset = 0;
		i++;
	}

	/* config arp */
	if (pdata->expansion.wol & WAKE_ARP) {
		memset(pattern[i].mask_info, 0, sizeof(pattern[0].mask_info));
		type_offset = offsetof(struct pattern_packet, ar_pro);
		pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8;
		type_offset++;
		pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8;
		op_offset = offsetof(struct pattern_packet, ar_op);
		pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8;
		op_offset++;
		pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8;
		tip_offset = offsetof(struct pattern_packet, ar_tip);
		pattern[i].mask_info[tip_offset / 8] |= 1 << tip_offset % 8;
		tip_offset++;
		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;
		tip_offset++;
		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;
		tip_offset++;
		pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8;

		packet.ar_pro =
			0x0 << 8 |
			0x08; /* arp type is 0x0800, notice that ar_pro and ar_op is big endian */
		packet.ar_op =
			0x1
			<< 8; /* 1 is arp request,2 is arp replay, 3 is rarp request, 4 is rarp replay */
		ip_addr = fxgmac_get_netdev_ip4addr(pdata);
		packet.ar_tip[0] = ip_addr & 0xFF;
		packet.ar_tip[1] = (ip_addr >> 8) & 0xFF;
		packet.ar_tip[2] = (ip_addr >> 16) & 0xFF;
		packet.ar_tip[3] = (ip_addr >> 24) & 0xFF;
		memcpy(pattern[i].pattern_info, &packet, MAX_PATTERN_SIZE);
		pattern[i].mask_size = sizeof(pattern[0].mask_info);
		pattern[i].pattern_offset = 0;
		i++;
	}

	fxgmac_set_wake_pattern(pdata, pattern, i);
}

static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata,
				    unsigned int wol)
{
	u32 regval = 0;

	fxgmac_disable_tx(pdata);
	fxgmac_disable_rx(pdata);

	/* performs fxgmac power down sequence
	 * 1. set led
	 * 2. check wol.
	 * 3. check arp offloading
	 * 4. disable gmac rx
	 * 5. set gmac power down
	 */

	/* Close LED when entering the S3, S4, S5 except solution3 */
	fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &regval);
	/* led index use bit0~bit5 */
	regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN);
	if (EFUSE_LED_COMMON_SOLUTION != regval) {
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED0_CFG);
		if (EFUSE_LED_SOLUTION3 == regval) {
			fxgmac_write_ephy_reg(
				pdata, REG_MII_EXT_DATA,
				REG_MII_EXT_COMMON_LED0_CFG_VALUE_SLEEP_SOLUTION3);
		} else {
			fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);
		}

		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED1_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);

		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
				      REG_MII_EXT_COMMON_LED2_CFG);
		fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00);
	}

	if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) {
		netdev_err(
			pdata->netdev,
			"fxgmac powerstate is %lu when config power to down.\n",
			pdata->expansion.powerstate);
	}

#if FXGMAC_WOL_FEATURE_ENABLED
	fxgmac_config_wol(pdata, wol);
#endif
#if FXGMAC_AOE_FEATURE_ENABLED
	/* use default arp offloading feature */
	fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL);
	fxgmac_enable_arp_offload(pdata);
#endif

#if FXGMAC_NS_OFFLOAD_ENABLED
	/* pls do not change the seq below */
	fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST);
	fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK);
	fxgmac_enable_ns_offload(pdata);
#endif

	/* Enable MAC Rx TX */
	if (1) {
		regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
		regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS,
					     MAC_CR_RE_LEN, 1);
		if (pdata->hw_feat.aoe) {
			regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS,
						     MAC_CR_TE_LEN, 1);
		}
		writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);
	}

	regval = readreg(pdata->pAdapter, pdata->base_mem + LPW_CTRL);
	regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_LPW_EN_POS,
				     LPW_CTRL_ASPM_LPW_EN_LEN,
				     1); /* Enable PCIE PM_L23. */

	writereg(pdata->pAdapter, regval, pdata->base_mem + LPW_CTRL);

	/* set gmac power down */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_PWRDWN_POS,
				     MAC_PMT_STA_PWRDWN_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);

	/* adjust sigdet threshold
	 * redmine.motor-comm.com/issues/5093
	 * fix issue can not wake up os on some FT-D2000 platform, y
	 * this modification is only temporarif it is 55mv, wol maybe failed.
	 */

	regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_SIGDET);
	regval = FXGMAC_SET_REG_BITS(regval, MGMT_SIGDET_POS, MGMT_SIGDET_LEN,
				     MGMT_SIGDET_40MV);
	writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_SIGDET);
	DPRINTK("fxgmac_config_powerdown callout, reg=0x%08x\n", regval);
}

static void fxgmac_config_powerup(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;

	if (test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) {
		netdev_err(
			pdata->netdev,
			"fxgmac powerstate is %lu when config power to up.\n",
			pdata->expansion.powerstate);
	}

	/* After enable OOB_WOL from efuse, mac will loopcheck phy status, and lead to panic sometimes.
	 * So we should disable it from powerup, enable it from power down.
	 */
	regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + OOB_WOL_CTRL);
	regval = FXGMAC_SET_REG_BITS(regval, OOB_WOL_CTRL_DIS_POS,
				     OOB_WOL_CTRL_DIS_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->base_mem + OOB_WOL_CTRL);

	/* clear wpi mode whether or not waked by WOL, write reset value */
	regval =
		(u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0);

	regval = FXGMAC_SET_REG_BITS(regval, MGMT_WPI_CTRL0_WPI_MODE_POS,
				     MGMT_WPI_CTRL0_WPI_MODE_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_WPI_CTRL0);
	/* read pmt_status register to De-assert the pmt_intr_o */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA);
	/* wether or not waked up by WOL, write reset value */
	regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_PWRDWN_POS,
				     MAC_PMT_STA_PWRDWN_LEN, 0);
	/* write register to synchronized always-on block */
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA);

	/* Disable fast link mode*/
	cfg_r32(pdata, REG_POWER_EIOS, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, POWER_EIOS_POS, POWER_EIOS_LEN, 0);
	cfg_w32(pdata, REG_POWER_EIOS, regval);

	fxgmac_pwr_clock_gate(pdata);
}

#if FXGMAC_SANITY_CHECK_ENABLED
/*
 * fxgmac_diag_sanity_check
 * check if there is any error like tx q hang
 * return: 0 normal and other fatal error
 */
static int fxgmac_diag_sanity_check(struct fxgmac_pdata *pdata)
{
	u32 reg_q_val, reg_tail_val;
	static u32 reg_tail_pre;
	static int cnt;

	reg_q_val = readreg(pdata->pAdapter,
			    FXGMAC_MTL_REG(pdata, 0 /* tx channe 0 */,
					   0x8 /*  0x2d08 */));
	if (!(reg_q_val & 0x10)) { /* tx q is empty */
		return 0;
	}
	reg_tail_val =
		readreg(pdata->pAdapter,
			FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_TDTR_LO));
	if (reg_tail_pre != reg_tail_val) {
		reg_tail_pre = reg_tail_val;
		cnt = 0;
	} else {
		cnt++;
	}

	if (cnt > 10) {
		reg_q_val = readreg(pdata->pAdapter,
				    FXGMAC_MTL_REG(pdata, 0 /* tx channe 0 */,
						   0x8 /*  0x2d08 */));
		if (reg_q_val & 0x10) { /* double check */
			DPRINTK("fxgmac, WARNing, tx Q status is 0x%x and tail keeps unchanged for %d times, 0x%x\n",
				reg_q_val, cnt, reg_tail_val);
			return 1;
		}
	}

	return 0;
}
#endif
static void fxgmac_pwr_clock_gate(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL1);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &regval);
	/* close pll in sleep mode */
	regval = FXGMAC_SET_REG_BITS(regval,
				     MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_POS,
				     MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_LEN,
				     0);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, regval);
}
static void fxgmac_pwr_clock_ungate(struct fxgmac_pdata *pdata)
{
	u32 regval = 0;

	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR,
			      REG_MII_EXT_SLEEP_CONTROL1);
	fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &regval);
	/* keep pll in sleep mode */
	regval = FXGMAC_SET_REG_BITS(regval,
				     MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_POS,
				     MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_LEN,
				     1);
	fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, regval);
}

/* context - pointer to struct nic_pdata. */
static unsigned char fxgmac_suspend_int(void *context)
{
	/* ULONG_PTR addr; */
	u32 intid;
#if FUXI_EPHY_INTERRUPT_D0_OFF
	u32 regval = 0;
#endif
	u32 val_mgmt_intcrtl0;
	struct fxgmac_pdata *pdata = (struct fxgmac_pdata *)context;

	val_mgmt_intcrtl0 =
		(u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0);
	/* disable  management interrupts. enable only pmt interrupts. */
	val_mgmt_intcrtl0 = FXGMAC_SET_REG_BITS(val_mgmt_intcrtl0,
						MGMT_INT_CTRL0_INT_MASK_POS,
						MGMT_INT_CTRL0_INT_MASK_LEN,
						MGMT_INT_CTRL0_INT_MASK_EX_PMT);
	writereg(pdata->pAdapter, val_mgmt_intcrtl0,
		 pdata->base_mem + MGMT_INT_CTRL0);

	for (intid = 0; intid < MSIX_TBL_MAX_NUM;
	     intid++) { /* disable all msix */
		writereg(pdata->pAdapter, 0x1,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}

	/* enable pmt msix */
	writereg(pdata->pAdapter, 0x0,
		 pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET +
			 MSI_ID_PHY_OTHER * 16);
	readreg(pdata->pAdapter,
		pdata->base_mem +
			MGMT_WOL_CTRL); /* read clear wake up reason */
	/* since Msix interrupt masked now, enable EPHY interrupt for case of link change wakeup */
	fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS,
			     NULL); /* clear phy interrupt */
#if FUXI_EPHY_INTERRUPT_D0_OFF
	regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS,
				     PHY_INT_MASK_LINK_UP_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS,
				     PHY_INT_MASK_LINK_DOWN_LEN, 1);
	fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK,
			      regval); /* enable phy interrupt */
#endif

	return true;
}
static int fxgmac_suspend_txrx(struct fxgmac_pdata *pdata)
{
	struct fxgmac_channel *channel;
	unsigned int i;
	u32 regval;
	int busy = 15;
	/* Prepare for Tx DMA channel stop */
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->tx_ring) {
			break;
		}
		fxgmac_prepare_tx_stop(pdata, channel);
	}

	/* Disable each Tx DMA channel */
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->tx_ring) {
			break;
		}

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS,
					     DMA_CH_TCR_ST_LEN, 0);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_TCR));
		DBGPRINT(MP_TRACE, (" %s disable tx  dma", __FUNCTION__));
	}

	do {
		regval =
			readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA);
		busy--;
	} while ((regval & MAC_DBG_STA_TX_BUSY) && (busy));

	if (0 != (regval & MAC_DBG_STA_TX_BUSY)) {
		regval =
			readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA);
		DbgPrintF(MP_WARN,
			  "warning !!!timed out waiting for Tx MAC to stop\n");
		return -1;
	}
	/* wait  empty Tx queue */
	for (i = 0; i < pdata->tx_q_count; i++) {
		do {
			regval = readreg(pdata->pAdapter,
					 FXGMAC_MTL_REG(pdata, i, MTL_TXQ_DEG));
			busy--;
		} while ((regval & MTL_TXQ_DEG_TX_BUSY) && (busy));
		if (0 != (regval & MTL_TXQ_DEG_TX_BUSY)) {
			regval = readreg(pdata->pAdapter,
					 pdata->mac_regs + MTL_TXQ_DEG);
			DbgPrintF(
				MP_WARN,
				"warning !!!timed out waiting for tx queue %u to empty\n",
				i);
			return -1;
		}
	}

	/* Disable MAC TxRx */
	regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 0);
	regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 0);
	writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR);

	/* Prepare for Rx DMA channel stop */
	for (i = 0; i < pdata->rx_q_count; i++) {
		fxgmac_prepare_rx_stop(pdata, i);
	}
	/* Disable each Rx DMA channel */
	channel = pdata->channel_head;
	for (i = 0; i < pdata->channel_count; i++, channel++) {
		if (!channel->rx_ring) {
			break;
		}

		regval = readreg(pdata->pAdapter,
				 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS,
					     DMA_CH_RCR_SR_LEN, 0);
		writereg(pdata->pAdapter, regval,
			 FXGMAC_DMA_REG(channel, DMA_CH_RCR));
		DBGPRINT(MP_TRACE, (" %s disable rx  dma", __FUNCTION__));
	}
	return 0;
}
static void fxgmac_resume_int(struct fxgmac_pdata *pdata)
{
	u32 intid, regval = 0;
	u32 val_mgmt_intcrtl0;

	val_mgmt_intcrtl0 =
		(u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0);
	/* disable management interrupts. enable only pmt interrupts. */
	val_mgmt_intcrtl0 = FXGMAC_SET_REG_BITS(
		val_mgmt_intcrtl0, MGMT_INT_CTRL0_INT_MASK_POS,
		MGMT_INT_CTRL0_INT_MASK_LEN, MGMT_INT_CTRL0_INT_MASK_DISABLE);
	writereg(pdata->pAdapter, val_mgmt_intcrtl0,
		 pdata->base_mem + MGMT_INT_CTRL0);

	for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) {
		writereg(pdata->pAdapter, 0,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}

	for (intid = MSIX_TBL_RXTX_NUM; intid < MSIX_TBL_MAX_NUM;
	     intid++) { /* disable some msix */
		writereg(pdata->pAdapter, 0,
			 pdata->base_mem + MSIX_TBL_BASE_ADDR +
				 MSIX_TBL_MASK_OFFSET + intid * 16);
	}

#if FUXI_EPHY_INTERRUPT_D0_OFF
	fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK,
			      0x0); /* disable phy interrupt */
	fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS,
			     NULL); /* clear phy interrupt */
#else
	regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS,
				     PHY_INT_MASK_LINK_UP_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS,
				     PHY_INT_MASK_LINK_DOWN_LEN, 1);
	fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK,
			      regval); /* enable phy interrupt */
#endif
}

static int fxgmac_hw_init(struct fxgmac_pdata *pdata)
{
	struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops;
	int ret;
	u32 regval = 0;

	if (netif_msg_drv(pdata)) {
		DPRINTK("fxgmac hw init call in\n");
	}

	/* Flush Tx queues */
	ret = fxgmac_flush_tx_queues(pdata);
	if (ret) {
		if (netif_msg_drv(pdata)) {
			DPRINTK("fxgmac_hw_init call flush tx queue err.\n");
		}
		return ret;
	}

	/* Initialize DMA related features */
	fxgmac_config_dma_bus(pdata);
	fxgmac_config_osp_mode(pdata);
	fxgmac_config_pblx8(pdata);
	fxgmac_config_tx_pbl_val(pdata);
	fxgmac_config_rx_pbl_val(pdata);
	fxgmac_config_rx_coalesce(pdata);
	fxgmac_config_tx_coalesce(pdata);
	fxgmac_config_rx_buffer_size(pdata);
	fxgmac_config_tso_mode(pdata);
	fxgmac_config_sph_mode(pdata);
	fxgmac_config_rss(pdata);
	fxgmac_config_wol(pdata, pdata->expansion.wol);

	desc_ops->tx_desc_init(pdata);
	desc_ops->rx_desc_init(pdata);
	fxgmac_enable_dma_interrupts(pdata);

	/* Initialize MTL related features */
	fxgmac_config_mtl_mode(pdata);
	fxgmac_config_queue_mapping(pdata);
	fxgmac_config_tsf_mode(pdata, pdata->tx_sf_mode);
	fxgmac_config_rsf_mode(pdata, pdata->rx_sf_mode);
	fxgmac_config_tx_threshold(pdata, pdata->tx_threshold);
	fxgmac_config_rx_threshold(pdata, pdata->rx_threshold);
	fxgmac_config_tx_fifo_size(pdata);
	fxgmac_config_rx_fifo_size(pdata);
	fxgmac_config_flow_control_threshold(pdata);
	fxgmac_config_rx_fep_disable(pdata);
	fxgmac_config_rx_fup_enable(pdata);
	fxgmac_enable_mtl_interrupts(pdata);

	/* Initialize MAC related features */
	fxgmac_config_mac_address(pdata);
	fxgmac_config_crc_check(pdata);
	fxgmac_config_rx_mode(pdata);
	fxgmac_config_jumbo(pdata);
	fxgmac_config_flow_control(pdata);
	fxgmac_config_mac_speed(pdata);
	fxgmac_config_checksum_offload(pdata);
	fxgmac_config_vlan_support(pdata);
	fxgmac_config_mmc(pdata);
	fxgmac_enable_mac_interrupts(pdata);

	/* enable EPhy link change interrupt */
	fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS,
			     NULL); /* clear  phy interrupt */
	regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS,
				     PHY_INT_MASK_LINK_UP_LEN, 1);
	regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS,
				     PHY_INT_MASK_LINK_DOWN_LEN, 1);
	fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK,
			      regval); /* enable phy interrupt */

	if (netif_msg_drv(pdata)) {
		DPRINTK("fxgmac hw init callout\n");
	}
	return 0;
}

static void fxgmac_save_nonstick_reg(struct fxgmac_pdata *pdata)
{
	u32 i;
	for (i = REG_PCIE_TRIGGER; i < MSI_PBA_REG; i += 4) {
		pdata->reg_nonstick[(i - REG_PCIE_TRIGGER) >> 2] =
			readreg(pdata->pAdapter, pdata->base_mem + i);
	}
}

static void fxgmac_restore_nonstick_reg(struct fxgmac_pdata *pdata)
{
	u32 i;
	for (i = REG_PCIE_TRIGGER; i < MSI_PBA_REG; i += 4) {
		writereg(pdata->pAdapter,
			 pdata->reg_nonstick[(i - REG_PCIE_TRIGGER) >> 2],
			 pdata->base_mem + i);
	}
}

static void fxgmac_esd_restore_pcie_cfg(struct fxgmac_pdata *pdata)
{
	cfg_w32(pdata, REG_PCI_COMMAND, pdata->expansion.cfg_pci_cmd);
	cfg_w32(pdata, REG_CACHE_LINE_SIZE,
		pdata->expansion.cfg_cache_line_size);
	cfg_w32(pdata, REG_MEM_BASE, pdata->expansion.cfg_mem_base);
	cfg_w32(pdata, REG_MEM_BASE_HI, pdata->expansion.cfg_mem_base_hi);
	cfg_w32(pdata, REG_IO_BASE, pdata->expansion.cfg_io_base);
	cfg_w32(pdata, REG_INT_LINE, pdata->expansion.cfg_int_line);
	cfg_w32(pdata, REG_DEVICE_CTRL1, pdata->expansion.cfg_device_ctrl1);
	cfg_w32(pdata, REG_PCI_LINK_CTRL, pdata->expansion.cfg_pci_link_ctrl);
	cfg_w32(pdata, REG_DEVICE_CTRL2, pdata->expansion.cfg_device_ctrl2);
	cfg_w32(pdata, REG_MSIX_CAPABILITY,
		pdata->expansion.cfg_msix_capability);
}

static int fxgmac_hw_exit(struct fxgmac_pdata *pdata)
{
	u32 regval;
	u32 value = 0;

	cfg_r32(pdata, REG_PCI_LINK_CTRL, &regval);
	pdata->pcie_link_status =
		FXGMAC_GET_REG_BITS(regval, PCI_LINK_CTRL_ASPM_CONTROL_POS,
				    PCI_LINK_CTRL_ASPM_CONTROL_LEN);
	if (PCI_LINK_CTRL_L1_STATUS == (pdata->pcie_link_status & 0x02)) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PCI_LINK_CTRL_ASPM_CONTROL_POS,
					     PCI_LINK_CTRL_ASPM_CONTROL_LEN, 0);
		cfg_w32(pdata, REG_PCI_LINK_CTRL, regval);
	}

	/* Issue a CHIP reset */
	regval = readreg(pdata->pAdapter, pdata->base_mem + SYS_RESET_REG);
	DPRINTK("CHIP_RESET 0x%x\n", regval);
	/* reg152c  bit31 1->reset, self-clear, if read it again, it still set 1. */
	regval = FXGMAC_SET_REG_BITS(regval, SYS_RESET_POS, SYS_RESET_LEN, 1);
	writereg(pdata->pAdapter, regval, pdata->base_mem + SYS_RESET_REG);

	usleep_range_ex(pdata->pAdapter, 9000, 10000);

	/* reg152c reset will reset trigger circuit and reload efuse patch 0x1004=0x16, need to release ephy reset again */
	value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS,
				    MGMT_EPHY_CTRL_RESET_LEN,
				    MGMT_EPHY_CTRL_STA_EPHY_RELEASE);
	writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL);
	usleep_range_ex(pdata->pAdapter, 100, 150);

	fxgmac_restore_nonstick_reg(
		pdata); /* reset will clear nonstick registers. */

	return 0;
}

static int fxgmac_set_gmac_register(struct fxgmac_pdata *pdata, u8 *address,
				    unsigned int data)
{
	if (address < (u8 *)(pdata->base_mem)) {
		return -1;
	}
	writereg(pdata->pAdapter, data, address);
	return 0;
}

static u32 fxgmac_get_gmac_register(struct fxgmac_pdata *pdata, u8 *address)
{
	u32 regval = 0;

	if (address > (u8 *)(pdata->base_mem)) {
		regval = readreg(pdata->pAdapter, address);
	}
	return regval;
}

static int fxgmac_pcie_init(struct fxgmac_pdata *pdata, bool ltr_en,
			    bool aspm_l1ss_en, bool aspm_l1_en,
			    bool aspm_l0s_en)
{
	u32 regval = 0;
	u32 deviceid = 0;

	cfg_r32(pdata, REG_PCI_LINK_CTRL, &regval);
	if (PCI_LINK_CTRL_L1_STATUS == (pdata->pcie_link_status & 0x02) &&
	    0x00 == FXGMAC_GET_REG_BITS(regval, PCI_LINK_CTRL_ASPM_CONTROL_POS,
					PCI_LINK_CTRL_ASPM_CONTROL_LEN)) {
		regval = FXGMAC_SET_REG_BITS(regval,
					     PCI_LINK_CTRL_ASPM_CONTROL_POS,
					     PCI_LINK_CTRL_ASPM_CONTROL_LEN,
					     pdata->pcie_link_status);
		cfg_w32(pdata, REG_PCI_LINK_CTRL, regval);
	}

	regval = FXGMAC_SET_REG_BITS(0, LTR_IDLE_ENTER_REQUIRE_POS,
				     LTR_IDLE_ENTER_REQUIRE_LEN,
				     LTR_IDLE_ENTER_REQUIRE);
	regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_ENTER_SCALE_POS,
				     LTR_IDLE_ENTER_SCALE_LEN,
				     LTR_IDLE_ENTER_SCALE);
	regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_ENTER_POS,
				     LTR_IDLE_ENTER_LEN, LTR_IDLE_ENTER_USVAL);
	regval = (regval << 16) + regval; /* snoopy + non-snoopy */
	writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_IDLE_ENTER);

	regval = 0;
	regval = FXGMAC_SET_REG_BITS(0, LTR_IDLE_EXIT_REQUIRE_POS,
				     LTR_IDLE_EXIT_REQUIRE_LEN,
				     LTR_IDLE_EXIT_REQUIRE);
	regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_EXIT_SCALE_POS,
				     LTR_IDLE_EXIT_SCALE_LEN,
				     LTR_IDLE_EXIT_SCALE);
	regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_EXIT_POS,
				     LTR_IDLE_EXIT_LEN, LTR_IDLE_EXIT_USVAL);
	regval = (regval << 16) + regval; /* snoopy + non-snoopy */
	writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_IDLE_EXIT);

	regval = readreg(pdata->pAdapter, pdata->base_mem + LTR_CTRL);
	if (ltr_en) {
		regval = FXGMAC_SET_REG_BITS(regval, LTR_CTRL_EN_POS,
					     LTR_CTRL_EN_LEN, 1);
		regval = FXGMAC_SET_REG_BITS(regval,
					     LTR_CTRL_IDLE_THRE_TIMER_POS,
					     LTR_CTRL_IDLE_THRE_TIMER_LEN,
					     LTR_CTRL_IDLE_THRE_TIMER_VAL);
	} else {
		regval = FXGMAC_SET_REG_BITS(regval, LTR_CTRL_EN_POS,
					     LTR_CTRL_EN_LEN, 0);
	}
	writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_CTRL);

	regval = readreg(pdata->pAdapter, pdata->base_mem + LPW_CTRL);
	regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_L0S_EN_POS,
				     LPW_CTRL_ASPM_L0S_EN_LEN,
				     aspm_l0s_en ? 1 : 0);
	regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_L1_EN_POS,
				     LPW_CTRL_ASPM_L1_EN_LEN,
				     aspm_l1_en ? 1 : 0);
	regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_L1SS_EN_POS,
				     LPW_CTRL_L1SS_EN_LEN,
				     aspm_l1ss_en ? 1 : 0);
	writereg(pdata->pAdapter, regval, pdata->base_mem + LPW_CTRL);

	cfg_r32(pdata, REG_ASPM_CONTROL, &regval);
	regval = FXGMAC_SET_REG_BITS(regval, ASPM_L1_IDLE_THRESHOLD_POS,
				     ASPM_L1_IDLE_THRESHOLD_LEN,
				     ASPM_L1_IDLE_THRESHOLD_1US);
	cfg_w32(pdata, REG_ASPM_CONTROL, regval);

	regval = 0;
	regval = FXGMAC_SET_REG_BITS(regval, PCIE_SERDES_PLL_AUTOOFF_POS,
				     PCIE_SERDES_PLL_AUTOOFF_LEN, 1);
	writereg(pdata->pAdapter, regval,
		 pdata->base_mem + REG_PCIE_SERDES_PLL);

	/*fuxi nto adjust sigdet threshold*/
	cfg_r8(pdata, REG_PCI_REVID, &regval);
	cfg_r16(pdata, REG_PCI_DEVICE_ID, &deviceid);
	if (FUXI_REV_01 == regval && PCI_DEVICE_ID_FUXI == deviceid) {
		regval =
			readreg(pdata->pAdapter, pdata->base_mem + MGMT_SIGDET);
		regval = FXGMAC_SET_REG_BITS(regval, MGMT_SIGDET_POS,
					     MGMT_SIGDET_LEN, MGMT_SIGDET_55MV);
		writereg(pdata->pAdapter, regval,
			 pdata->base_mem + MGMT_SIGDET);
	}

	return 0;
}

static void fxgmac_trigger_pcie(struct fxgmac_pdata *pdata, u32 code)
{
	writereg(pdata->pAdapter, code, pdata->base_mem + REG_PCIE_TRIGGER);
}

void fxgmac_init_hw_ops(struct fxgmac_hw_ops *hw_ops)
{
	hw_ops->init = fxgmac_hw_init;
	hw_ops->exit = fxgmac_hw_exit;
	hw_ops->save_nonstick_reg = fxgmac_save_nonstick_reg;
	hw_ops->restore_nonstick_reg = fxgmac_restore_nonstick_reg;
	hw_ops->esd_restore_pcie_cfg = fxgmac_esd_restore_pcie_cfg;

	hw_ops->set_gmac_register = fxgmac_set_gmac_register;
	hw_ops->get_gmac_register = fxgmac_get_gmac_register;

	hw_ops->tx_complete = fxgmac_tx_complete;
	hw_ops->enable_tx = fxgmac_enable_tx;
	hw_ops->disable_tx = fxgmac_disable_tx;
	hw_ops->enable_rx = fxgmac_enable_rx;
	hw_ops->disable_rx = fxgmac_disable_rx;
	hw_ops->enable_channel_rx = fxgmac_enable_channel_rx;
	hw_ops->dev_xmit = fxgmac_dev_xmit;
	hw_ops->dev_read = fxgmac_dev_read;
	hw_ops->config_tso = fxgmac_config_tso_mode;
	hw_ops->enable_int = fxgmac_enable_int;
	hw_ops->disable_int = fxgmac_disable_int;
	hw_ops->set_interrupt_moderation = fxgmac_set_interrupt_moderation;
	hw_ops->enable_msix_rxtxinterrupt = fxgmac_enable_msix_rxtxinterrupt;
	hw_ops->disable_msix_interrupt = fxgmac_disable_msix_interrupt;
	hw_ops->enable_msix_rxtxphyinterrupt =
		fxgmac_enable_msix_rxtxphyinterrupt;
	hw_ops->enable_msix_one_interrupt = fxgmac_enable_msix_one_interrupt;
	hw_ops->disable_msix_one_interrupt = fxgmac_disable_msix_one_interrupt;
	hw_ops->enable_mgm_interrupt = fxgmac_enable_mgm_interrupt;
	hw_ops->disable_mgm_interrupt = fxgmac_disable_mgm_interrupt;

	hw_ops->set_mac_address = fxgmac_set_mac_address;
	hw_ops->set_mac_hash = fxgmac_add_mac_addresses;
	hw_ops->config_rx_mode = fxgmac_config_rx_mode;
	hw_ops->enable_rx_csum = fxgmac_enable_rx_csum;
	hw_ops->disable_rx_csum = fxgmac_disable_rx_csum;

	/* For MII speed configuration */
	hw_ops->config_mac_speed = fxgmac_config_mac_speed;
	hw_ops->get_xlgmii_phy_status = fxgmac_check_phy_link;

	/* For descriptor related operation */
	hw_ops->tx_desc_init = fxgmac_tx_desc_init;
	hw_ops->rx_desc_init = fxgmac_rx_desc_init;
	hw_ops->tx_desc_reset = fxgmac_tx_desc_reset;
	hw_ops->rx_desc_reset = fxgmac_rx_desc_reset;
	hw_ops->is_last_desc = fxgmac_is_last_desc;
	hw_ops->is_context_desc = fxgmac_is_context_desc;
	hw_ops->tx_start_xmit = fxgmac_tx_start_xmit;
	hw_ops->set_pattern_data = fxgmac_set_pattern_data;
	hw_ops->config_wol = fxgmac_config_wol;
	hw_ops->get_rss_hash_key = fxgmac_read_rss_hash_key;
	hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table;
#if FXGMAC_SANITY_CHECK_ENABLED
	hw_ops->diag_sanity_check = fxgmac_diag_sanity_check;
#endif

	/* For Flow Control */
	hw_ops->config_tx_flow_control = fxgmac_config_tx_flow_control;
	hw_ops->config_rx_flow_control = fxgmac_config_rx_flow_control;

	/*For Jumbo Frames*/
	hw_ops->enable_jumbo = fxgmac_config_jumbo;

	/* For Vlan related config */
	hw_ops->enable_tx_vlan = fxgmac_enable_tx_vlan;
	hw_ops->disable_tx_vlan = fxgmac_disable_tx_vlan;
	hw_ops->enable_rx_vlan_stripping = fxgmac_enable_rx_vlan_stripping;
	hw_ops->disable_rx_vlan_stripping = fxgmac_disable_rx_vlan_stripping;
	hw_ops->enable_rx_vlan_filtering = fxgmac_enable_rx_vlan_filtering;
	hw_ops->disable_rx_vlan_filtering = fxgmac_disable_rx_vlan_filtering;
	hw_ops->update_vlan_hash_table = fxgmac_update_vlan_hash_table;

	/* For RX coalescing */
	hw_ops->config_rx_coalesce = fxgmac_config_rx_coalesce;
	hw_ops->config_tx_coalesce = fxgmac_config_tx_coalesce;
	hw_ops->usec_to_riwt = fxgmac_usec_to_riwt;
	hw_ops->riwt_to_usec = fxgmac_riwt_to_usec;

	/* For RX and TX threshold config */
	hw_ops->config_rx_threshold = fxgmac_config_rx_threshold;
	hw_ops->config_tx_threshold = fxgmac_config_tx_threshold;

	/* For RX and TX Store and Forward Mode config */
	hw_ops->config_rsf_mode = fxgmac_config_rsf_mode;
	hw_ops->config_tsf_mode = fxgmac_config_tsf_mode;

	/* For TX DMA Operating on Second Frame config */
	hw_ops->config_osp_mode = fxgmac_config_osp_mode;

	/* For RX and TX PBL config */
	hw_ops->config_rx_pbl_val = fxgmac_config_rx_pbl_val;
	hw_ops->get_rx_pbl_val = fxgmac_get_rx_pbl_val;
	hw_ops->config_tx_pbl_val = fxgmac_config_tx_pbl_val;
	hw_ops->get_tx_pbl_val = fxgmac_get_tx_pbl_val;
	hw_ops->config_pblx8 = fxgmac_config_pblx8;

	/* For MMC statistics support */
	hw_ops->tx_mmc_int = fxgmac_tx_mmc_int;
	hw_ops->rx_mmc_int = fxgmac_rx_mmc_int;
	hw_ops->read_mmc_stats = fxgmac_read_mmc_stats;

	/* For Receive Side Scaling */
	hw_ops->enable_rss = fxgmac_enable_rss;
	hw_ops->disable_rss = fxgmac_disable_rss;
	hw_ops->get_rss_options = fxgmac_read_rss_options;
	hw_ops->set_rss_options = fxgmac_write_rss_options;
	hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key;
	hw_ops->set_rss_lookup_table = fxgmac_set_rss_lookup_table;

	/*For Offload*/
	hw_ops->set_arp_offload = fxgmac_update_aoe_ipv4addr;
	hw_ops->enable_arp_offload = fxgmac_enable_arp_offload;
	hw_ops->disable_arp_offload = fxgmac_disable_arp_offload;

	hw_ops->set_ns_offload = fxgmac_set_ns_offload;
	hw_ops->enable_ns_offload = fxgmac_enable_ns_offload;
	hw_ops->disable_ns_offload = fxgmac_disable_ns_offload;

	hw_ops->enable_wake_magic_pattern = fxgmac_enable_wake_magic_pattern;
	hw_ops->disable_wake_magic_pattern = fxgmac_disable_wake_magic_pattern;

	hw_ops->enable_wake_link_change = fxgmac_enable_wake_link_change;
	hw_ops->disable_wake_link_change = fxgmac_disable_wake_link_change;

	hw_ops->check_wake_pattern_fifo_pointer =
		fxgmac_check_wake_pattern_fifo_pointer;
	hw_ops->set_wake_pattern = fxgmac_set_wake_pattern;
	hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern;
	hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern;
	hw_ops->set_wake_pattern_mask = fxgmac_set_wake_pattern_mask;
#if defined(FUXI_PM_WPI_READ_FEATURE_EN) && FUXI_PM_WPI_READ_FEATURE_EN
	hw_ops->enable_wake_packet_indication =
		fxgmac_enable_wake_packet_indication;
	hw_ops->get_wake_packet_indication = fxgmac_get_wake_packet_indication;
#endif

	/*For phy write /read*/
	hw_ops->reset_phy = fxgmac_reset_phy;
	hw_ops->release_phy = fxgmac_release_phy;
	hw_ops->get_ephy_state = fxgmac_get_ephy_state;
	hw_ops->write_ephy_reg = fxgmac_write_ephy_reg;
	hw_ops->read_ephy_reg = fxgmac_read_ephy_reg;
	hw_ops->set_ephy_autoneg_advertise = fxgmac_set_ephy_autoneg_advertise;
	hw_ops->phy_config = fxgmac_phy_config;
	hw_ops->close_phy_led = fxgmac_close_phy_led;
	hw_ops->led_under_active = fxmgac_config_led_under_active;
	hw_ops->led_under_sleep = fxgmac_config_led_under_sleep;
	hw_ops->led_under_shutdown = fxgmac_config_led_under_shutdown;
	hw_ops->led_under_disable = fxgmac_config_led_under_disable;
	hw_ops->enable_phy_check = fxgmac_enable_phy_check;
	hw_ops->disable_phy_check = fxgmac_disable_phy_check;
	hw_ops->setup_cable_loopback = fxgmac_setup_cable_loopback;
	hw_ops->clean_cable_loopback = fxgmac_clean_cable_loopback;
	hw_ops->disable_phy_sleep = fxgmac_disable_phy_sleep;
	hw_ops->enable_phy_sleep = fxgmac_enable_phy_sleep;
	hw_ops->phy_green_ethernet = fxgmac_phy_green_ethernet;
	hw_ops->phy_eee_feature = fxgmac_phy_eee_feature;

	/* For power management */
	hw_ops->pre_power_down = fxgmac_pre_powerdown;
	hw_ops->config_power_down = fxgmac_config_powerdown;
	hw_ops->config_power_up = fxgmac_config_powerup;
	hw_ops->set_suspend_int = fxgmac_suspend_int;
	hw_ops->set_resume_int = fxgmac_resume_int;
	hw_ops->set_suspend_txrx = fxgmac_suspend_txrx;
	hw_ops->set_pwr_clock_gate = fxgmac_pwr_clock_gate;
	hw_ops->set_pwr_clock_ungate = fxgmac_pwr_clock_ungate;

	hw_ops->set_all_multicast_mode = fxgmac_set_all_multicast_mode;
	hw_ops->config_multicast_mac_hash_table =
		fxgmac_config_multicast_mac_hash_table;
	hw_ops->set_promiscuous_mode = fxgmac_set_promiscuous_mode;
	hw_ops->enable_rx_broadcast = fxgmac_enable_rx_broadcast;

	/* efuse relevant operation. */
	hw_ops->read_patch_from_efuse =
		fxgmac_read_patch_from_efuse; /* read patch per register. */
	hw_ops->read_patch_from_efuse_per_index =
		fxgmac_read_patch_from_efuse_per_index; /* read patch per index. */
	hw_ops->write_patch_to_efuse = fxgmac_write_patch_to_efuse;
	hw_ops->write_patch_to_efuse_per_index =
		fxgmac_write_patch_to_efuse_per_index;
	hw_ops->read_mac_subsys_from_efuse = fxgmac_read_mac_subsys_from_efuse;
	hw_ops->write_mac_subsys_to_efuse = fxgmac_write_mac_subsys_to_efuse;
	hw_ops->efuse_load = fxgmac_efuse_load;
	hw_ops->read_efuse_data = fxgmac_efuse_read_data;
	hw_ops->write_oob = fxgmac_efuse_write_oob;
	hw_ops->write_led = fxgmac_efuse_write_led;
	hw_ops->write_led_config = fxgmac_write_led_setting_to_efuse;
	hw_ops->read_led_config = fxgmac_read_led_setting_from_efuse;

	/*  */
	hw_ops->pcie_init = fxgmac_pcie_init;
	hw_ops->trigger_pcie = fxgmac_trigger_pcie;
}
